simple-module-settings 0.0.40__tar.gz → 0.0.41__tar.gz

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.
Files changed (126) hide show
  1. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/PKG-INFO +4 -4
  2. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/package.json +1 -1
  3. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/pyproject.toml +4 -4
  4. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/components/FieldSource.tsx +3 -3
  5. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/components/ModuleFieldRow.tsx +11 -11
  6. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/components/ModuleForm.tsx +2 -2
  7. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests-js/ModuleForm.test.tsx +1 -1
  8. simple_module_settings-0.0.41/tests-js/settings_ModuleFieldLayout.test.tsx +62 -0
  9. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/.gitignore +0 -0
  10. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/LICENSE +0 -0
  11. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/README.md +0 -0
  12. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/__init__.py +0 -0
  13. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/_announce.py +0 -0
  14. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/_field_meta.py +0 -0
  15. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/_listing.py +0 -0
  16. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/_managed_keys.py +0 -0
  17. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/_module_settings.py +0 -0
  18. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/_module_settings_props.py +0 -0
  19. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/_row_masking.py +0 -0
  20. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/_secrets.py +0 -0
  21. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/_unique_write.py +0 -0
  22. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/browse_query.py +0 -0
  23. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/cli.py +0 -0
  24. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/constants.py +0 -0
  25. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/contracts/__init__.py +0 -0
  26. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/contracts/accessor.py +0 -0
  27. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/contracts/events.py +0 -0
  28. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/contracts/invalidation.py +0 -0
  29. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/contracts/registry.py +0 -0
  30. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/contracts/schemas.py +0 -0
  31. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/deps.py +0 -0
  32. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/endpoints/__init__.py +0 -0
  33. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/endpoints/_create_form.py +0 -0
  34. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/endpoints/api.py +0 -0
  35. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/endpoints/module_api.py +0 -0
  36. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/endpoints/tenant_api.py +0 -0
  37. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/endpoints/views.py +0 -0
  38. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/env_vars.py +0 -0
  39. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/errors.py +0 -0
  40. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/exceptions.py +0 -0
  41. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/hydrate.py +0 -0
  42. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/invalidation.py +0 -0
  43. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/known_keys.py +0 -0
  44. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/locales/de.json +0 -0
  45. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/locales/en.json +0 -0
  46. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/models.py +0 -0
  47. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/module.py +0 -0
  48. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/module_registry.py +0 -0
  49. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/Browse.tsx +0 -0
  50. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/Create.tsx +0 -0
  51. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/Edit.tsx +0 -0
  52. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/ModulesEdit.tsx +0 -0
  53. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/api.generated.ts +0 -0
  54. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/api.ts +0 -0
  55. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/components/FieldInput.tsx +0 -0
  56. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/components/JsonInput.tsx +0 -0
  57. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/components/KeyField.tsx +0 -0
  58. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/components/ResolvedValue.tsx +0 -0
  59. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/components/ScopeTabs.tsx +0 -0
  60. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/components/StoreCards.tsx +0 -0
  61. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/components/StoreTable.tsx +0 -0
  62. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/components/TestConnectionButton.tsx +0 -0
  63. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/components/ValueInput.tsx +0 -0
  64. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/components/last-test.ts +0 -0
  65. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/components/validation-errors.test.ts +0 -0
  66. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/components/validation-errors.ts +0 -0
  67. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/routes.ts +0 -0
  68. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/pages/types.ts +0 -0
  69. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/py.typed +0 -0
  70. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/registration.py +0 -0
  71. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/reload.py +0 -0
  72. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/service.py +0 -0
  73. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/services.py +0 -0
  74. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/settings.py +0 -0
  75. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/store.py +0 -0
  76. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/tenant_scope.py +0 -0
  77. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/settings/tenant_view.py +0 -0
  78. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_clear_route_per_scope.py +0 -0
  79. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_cli_import.py +0 -0
  80. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_concurrent_overrides.py +0 -0
  81. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_duplicate_and_race.py +0 -0
  82. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_env_pinned.py +0 -0
  83. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_hydrate.py +0 -0
  84. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_hydrate_related_fields.py +0 -0
  85. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_known_keys_meta.py +0 -0
  86. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_lenient_related_fields_validators.py +0 -0
  87. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_module_api.py +0 -0
  88. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_module_api_json_fields.py +0 -0
  89. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_module_registry.py +0 -0
  90. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_module_settings.py +0 -0
  91. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_module_settings_render.py +0 -0
  92. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_module_settings_serialize.py +0 -0
  93. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_registration.py +0 -0
  94. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_reload.py +0 -0
  95. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_settings_accessor.py +0 -0
  96. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_settings_api.py +0 -0
  97. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_settings_commit_invalidation.py +0 -0
  98. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_settings_default_clears_override.py +0 -0
  99. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_settings_domain_errors.py +0 -0
  100. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_settings_env_unprefixed.py +0 -0
  101. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_settings_events.py +0 -0
  102. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_settings_field_sources.py +0 -0
  103. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_settings_framework_reexports.py +0 -0
  104. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_settings_group_metadata.py +0 -0
  105. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_settings_host_restart_flags.py +0 -0
  106. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_settings_module.py +0 -0
  107. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_settings_schemas.py +0 -0
  108. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_settings_secret_json_roundtrip.py +0 -0
  109. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_settings_secret_masking.py +0 -0
  110. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_settings_secret_urls.py +0 -0
  111. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_settings_service.py +0 -0
  112. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_settings_typed_secret_masking.py +0 -0
  113. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_settings_view_authz.py +0 -0
  114. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_store.py +0 -0
  115. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_store_filters.py +0 -0
  116. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_store_secret_masking.py +0 -0
  117. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_tenant_settings.py +0 -0
  118. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_testable_checks.py +0 -0
  119. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests/test_upload_key_delete.py +0 -0
  120. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests-js/JsonFields.test.tsx +0 -0
  121. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests-js/ModulesEdit.test.tsx +0 -0
  122. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests-js/RestartHint.test.tsx +0 -0
  123. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests-js/ScopeTabs.test.tsx +0 -0
  124. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests-js/settings_ModuleFormRevert.test.tsx +0 -0
  125. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tests-js/settings_TestConnectionButton.test.tsx +0 -0
  126. {simple_module_settings-0.0.40 → simple_module_settings-0.0.41}/tsconfig.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: simple_module_settings
3
- Version: 0.0.40
3
+ Version: 0.0.41
4
4
  Summary: Runtime settings UI — modules plug their own settings panels into a shared admin view
5
5
  Project-URL: Homepage, https://github.com/antosubash/simple_module_python
6
6
  Project-URL: Repository, https://github.com/antosubash/simple_module_python
@@ -21,9 +21,9 @@ Classifier: Topic :: Internet :: WWW/HTTP
21
21
  Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
22
22
  Classifier: Typing :: Typed
23
23
  Requires-Python: >=3.12
24
- Requires-Dist: simple-module-core==0.0.40
25
- Requires-Dist: simple-module-db==0.0.40
26
- Requires-Dist: simple-module-hosting==0.0.40
24
+ Requires-Dist: simple-module-core==0.0.41
25
+ Requires-Dist: simple-module-db==0.0.41
26
+ Requires-Dist: simple-module-hosting==0.0.41
27
27
  Requires-Dist: typer<0.28,>=0.12
28
28
  Description-Content-Type: text/markdown
29
29
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simple-module-py/settings",
3
- "version": "0.0.40",
3
+ "version": "0.0.41",
4
4
  "private": true,
5
5
  "description": "Frontend assets for the Settings module",
6
6
  "peerDependencies": {
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "simple_module_settings"
3
- version = "0.0.40"
3
+ version = "0.0.41"
4
4
  description = "Runtime settings UI — modules plug their own settings panels into a shared admin view"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -21,9 +21,9 @@ classifiers = [
21
21
  "Typing :: Typed",
22
22
  ]
23
23
  dependencies = [
24
- "simple_module_core==0.0.40",
25
- "simple_module_db==0.0.40",
26
- "simple_module_hosting==0.0.40",
24
+ "simple_module_core==0.0.41",
25
+ "simple_module_db==0.0.41",
26
+ "simple_module_hosting==0.0.41",
27
27
  "typer>=0.12,<0.28",
28
28
  ]
29
29
 
@@ -55,10 +55,10 @@ export function FieldSource({ field, onReset }: { field: FieldMeta; onReset: ()
55
55
  const showDefault = !isBlank(field.default) && String(field.default) !== String(field.value);
56
56
  return (
57
57
  // Wraps rather than clips: `SM_FILE_STORAGE_QUOTA_BYTES` has no space to
58
- // break at, so a 210px column cut it mid-word with nothing to say so.
59
- // `break-words` breaks only the tokens that do not fit.
58
+ // break at, and as an unbreakable token it would set the column's minimum
59
+ // width; `overflow-wrap:anywhere` lets it break inside the identifier.
60
60
  <span
61
- className="block break-words font-mono text-[11.5px] leading-snug text-muted-foreground"
61
+ className="block [overflow-wrap:anywhere] font-mono text-[11.5px] leading-snug text-muted-foreground"
62
62
  title={
63
63
  field.env_readable
64
64
  ? t(keys.settings.modules.env_var_hint)
@@ -16,10 +16,11 @@ interface Props {
16
16
  /**
17
17
  * One settings row: label, control, trailing meta.
18
18
  *
19
- * Three columns rather than a stacked label block, because the trailing column
20
- * is what makes the screen readable at a glance — every row answers "where is
21
- * this value from?" in the same place, so the exceptions stand out instead of
22
- * having to be hunted for.
19
+ * Three columns when the editor card has room, because the trailing column is
20
+ * what makes the screen readable at a glance — every row answers "where is
21
+ * this value from?" in the same place, so the exceptions stand out. The tracks
22
+ * follow the card's width (container query), not the viewport: two sidebars
23
+ * can leave a narrow card on a wide screen, and the row stacks instead.
23
24
  */
24
25
  export function ModuleFieldRow({
25
26
  field,
@@ -37,15 +38,14 @@ export function ModuleFieldRow({
37
38
  const showDescription = field.description && field.type !== 'bool';
38
39
 
39
40
  return (
40
- // `minmax(170px, auto)`: a fixed 170px track cannot grow, so a long field
41
- // name overflowed it and printed straight over the input beside it.
42
- <div className="grid gap-3 sm:grid-cols-[minmax(170px,auto)_1fr] lg:grid-cols-[minmax(170px,auto)_1fr_210px] lg:items-center">
41
+ // Query the editor card: two sidebars can leave little space at a wide viewport.
42
+ <div className="grid grid-cols-1 gap-3 @md:grid-cols-[minmax(0,140px)_minmax(180px,1fr)] @xl:grid-cols-[minmax(0,140px)_minmax(180px,1fr)_minmax(0,160px)] @xl:items-center">
43
43
  <div className="min-w-0">
44
- <label htmlFor={id} className="block break-words text-[13px] font-medium">
44
+ <label htmlFor={id} className="block [overflow-wrap:anywhere] text-[13px] font-medium">
45
45
  {field.name}
46
46
  </label>
47
47
  {showDescription && (
48
- <p className="mt-0.5 text-[11.5px] leading-snug text-muted-foreground">
48
+ <p className="mt-0.5 [overflow-wrap:anywhere] text-[11.5px] leading-snug text-muted-foreground">
49
49
  {field.description}
50
50
  </p>
51
51
  )}
@@ -65,7 +65,7 @@ export function ModuleFieldRow({
65
65
  </fieldset>
66
66
  </div>
67
67
 
68
- <div className="sm:col-span-2 lg:col-span-1 lg:justify-self-start">
68
+ <div className="min-w-0 [overflow-wrap:anywhere] @md:col-span-2 @xl:col-span-1 @xl:justify-self-start">
69
69
  <FieldSource field={field} onReset={onReset} />
70
70
  </div>
71
71
 
@@ -75,7 +75,7 @@ export function ModuleFieldRow({
75
75
  {error && (
76
76
  <p
77
77
  data-testid={`field-error-${field.name}`}
78
- className="break-words text-xs text-destructive sm:col-span-1 sm:col-start-2 lg:col-span-2"
78
+ className="min-w-0 [overflow-wrap:anywhere] text-xs text-destructive @md:col-span-1 @md:col-start-2 @xl:col-span-2"
79
79
  >
80
80
  {error}
81
81
  </p>
@@ -134,7 +134,7 @@ export function ModuleForm({ module: m, checks = [] }: Props) {
134
134
  }
135
135
 
136
136
  return (
137
- <div className="flex flex-1 flex-col gap-4">
137
+ <div className="flex min-w-0 flex-1 flex-col gap-4">
138
138
  {/* Outside the card: the heading names the pane, and the deck keeps the
139
139
  card for the fields alone. */}
140
140
  <header className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
@@ -178,7 +178,7 @@ export function ModuleForm({ module: m, checks = [] }: Props) {
178
178
  </div>
179
179
  )}
180
180
 
181
- <Card className="flex flex-1 flex-col gap-4 border-border p-6">
181
+ <Card className="@container flex min-w-0 flex-1 flex-col gap-4 border-border p-6">
182
182
  {/* No inner scroller: the card grows and the page scrolls, which is
183
183
  one scrollbar for the screen instead of three nested ones. */}
184
184
  <div className="flex flex-col gap-4">
@@ -105,7 +105,7 @@ describe('ModuleForm', () => {
105
105
  // control's cell, where it wrapped a word per line.
106
106
  const error = screen.getByText(/item rejected/);
107
107
  expect(error.contains(screen.getByLabelText('retention_days'))).toBe(false);
108
- expect(error.className).toContain('lg:col-span-2');
108
+ expect(error.className).toContain('@xl:col-span-2');
109
109
  });
110
110
 
111
111
  test('shows an error whose loc names no field at form level', async () => {
@@ -0,0 +1,62 @@
1
+ import { configureI18n } from '@simple-module-py/i18n';
2
+ import { render, screen } from '@testing-library/react';
3
+ import { expect, test } from 'vitest';
4
+ import type { FieldMeta } from '../settings/pages/components/FieldInput';
5
+ import { ModuleFieldRow } from '../settings/pages/components/ModuleFieldRow';
6
+ import { ModuleForm } from '../settings/pages/components/ModuleForm';
7
+
8
+ configureI18n({ locale: 'en', messages: {} });
9
+
10
+ const field: FieldMeta = {
11
+ name: 'very_long_underscore_separated_setting_name',
12
+ env_var: 'SM_FILE_STORAGE_VERY_LONG_UNDERSCORE_SEPARATED_SETTING_NAME',
13
+ type: 'string',
14
+ value: '',
15
+ default: '',
16
+ description: '',
17
+ is_secret: false,
18
+ requires_restart: false,
19
+ group: null,
20
+ };
21
+
22
+ // jsdom has no layout engine: these guard the responsive CSS contract.
23
+ test('the editor card supplies the field rows container width', () => {
24
+ const { container } = render(
25
+ <ModuleForm
26
+ module={{
27
+ module_name: 'FileStorage',
28
+ package: 'file_storage',
29
+ env_prefix: 'SM_FILE_STORAGE_',
30
+ class_name: 'FileStorageSettings',
31
+ fields: [field],
32
+ }}
33
+ />,
34
+ );
35
+ expect(container.querySelector('[data-slot="card"]')).toHaveClass('@container', 'min-w-0');
36
+ });
37
+
38
+ test('rows bound their tracks and wrap identifiers without viewport breakpoints', () => {
39
+ const { container } = render(
40
+ <ModuleFieldRow
41
+ field={field}
42
+ package="file_storage"
43
+ value=""
44
+ modified={false}
45
+ error="Invalid setting"
46
+ onChange={() => {}}
47
+ onReset={() => {}}
48
+ />,
49
+ );
50
+ const row = container.firstElementChild;
51
+ expect(row).toHaveClass(
52
+ 'grid-cols-1',
53
+ '@md:grid-cols-[minmax(0,140px)_minmax(180px,1fr)]',
54
+ '@xl:grid-cols-[minmax(0,140px)_minmax(180px,1fr)_minmax(0,160px)]',
55
+ );
56
+ expect(row?.className).not.toMatch(/(?:^|\s)(?:sm|lg):/);
57
+ expect(screen.getByText(field.name)).toHaveClass('[overflow-wrap:anywhere]');
58
+ const source = screen.getByText(field.env_var);
59
+ expect(source).toHaveClass('[overflow-wrap:anywhere]');
60
+ expect(source.parentElement).toHaveClass('min-w-0', '@md:col-span-2', '@xl:col-span-1');
61
+ expect(screen.getByText('Invalid setting')).toHaveClass('@md:col-start-2', '@xl:col-span-2');
62
+ });