@qoretechnologies/reqraft 0.10.57 → 0.10.58-pr.129.g4b46b32
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/design/FORM_ENGINE_COMPACT.md +23 -5
- package/dist/components/form/engine/CompactToolbar.d.ts.map +1 -1
- package/dist/components/form/engine/CompactToolbar.js +2 -2
- package/dist/components/form/engine/CompactToolbar.js.map +1 -1
- package/dist/components/form/engine/FormEngine.d.ts.map +1 -1
- package/dist/components/form/engine/FormEngine.js +35 -8
- package/dist/components/form/engine/FormEngine.js.map +1 -1
- package/dist/components/form/engine/OptionFieldMessages.d.ts.map +1 -1
- package/dist/components/form/engine/OptionFieldMessages.js +6 -3
- package/dist/components/form/engine/OptionFieldMessages.js.map +1 -1
- package/dist/components/form/engine/variants/VariantCalmTable.d.ts.map +1 -1
- package/dist/components/form/engine/variants/VariantCalmTable.js +1 -1
- package/dist/components/form/engine/variants/VariantCalmTable.js.map +1 -1
- package/dist/components/form/engine/variants/variantModel.d.ts.map +1 -1
- package/dist/components/form/engine/variants/variantModel.js +8 -3
- package/dist/components/form/engine/variants/variantModel.js.map +1 -1
- package/package.json +1 -1
- package/src/components/form/engine/CompactToolbar.tsx +8 -3
- package/src/components/form/engine/FormEngine.stories.tsx +140 -11
- package/src/components/form/engine/FormEngine.tsx +38 -8
- package/src/components/form/engine/OptionFieldMessages.tsx +7 -5
- package/src/components/form/engine/variants/VariantCalmTable.tsx +3 -1
- package/src/components/form/engine/variants/variantModel.ts +10 -3
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.summarize = exports.buildVariantGroups = void 0;
|
|
4
4
|
var readFirst_1 = require("../readFirst");
|
|
5
5
|
var templates_1 = require("../../../../helpers/templates");
|
|
6
|
+
var validations_1 = require("../../../../helpers/validations");
|
|
6
7
|
// Default (basic-fixture) invalid set — overridable via config.
|
|
7
8
|
var DEFAULT_INVALID = {
|
|
8
9
|
optionWithInvalidValue: 'Text value is empty',
|
|
@@ -67,8 +68,11 @@ function buildVariantGroups(options, values, config) {
|
|
|
67
68
|
var empty = (0, readFirst_1.isOptionValueEmpty)(field === null || field === void 0 ? void 0 : field.value);
|
|
68
69
|
var ownGroups = (schema === null || schema === void 0 ? void 0 : schema.required_groups) || [];
|
|
69
70
|
// Member of a one-of group that nobody has satisfied yet.
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
// A field locked by an unmet `depends_on` does not apply yet, so it is
|
|
72
|
+
// neither required nor part of an unmet group — the engine's rule.
|
|
73
|
+
var locked = !!(schema === null || schema === void 0 ? void 0 : schema.depends_on) && !(0, validations_1.hasAllDependenciesFullfilled)(schema.depends_on, values, options);
|
|
74
|
+
var inUnmetGroup = !locked && ownGroups.some(function (k) { return reqGroups[k] && !reqGroups[k].satisfied; });
|
|
75
|
+
var required = !locked && !!(schema === null || schema === void 0 ? void 0 : schema.required);
|
|
72
76
|
var status;
|
|
73
77
|
var reason;
|
|
74
78
|
if (invalidReasons[name]) {
|
|
@@ -99,7 +103,8 @@ function buildVariantGroups(options, values, config) {
|
|
|
99
103
|
label: (schema === null || schema === void 0 ? void 0 : schema.display_name) || name,
|
|
100
104
|
shortDesc: schema === null || schema === void 0 ? void 0 : schema.short_desc,
|
|
101
105
|
longDesc: schema === null || schema === void 0 ? void 0 : schema.desc,
|
|
102
|
-
|
|
106
|
+
// The asterisk still marks it: it is required once it applies.
|
|
107
|
+
required: !!(schema === null || schema === void 0 ? void 0 : schema.required) || ownGroups.length > 0,
|
|
103
108
|
readOnly: !!(schema === null || schema === void 0 ? void 0 : schema.readonly),
|
|
104
109
|
status: status,
|
|
105
110
|
reason: reason,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"variantModel.js","sourceRoot":"","sources":["../../../../../src/components/form/engine/variants/variantModel.ts"],"names":[],"mappings":";;;AAWA,0CASsB;AACtB,2DAAgE;
|
|
1
|
+
{"version":3,"file":"variantModel.js","sourceRoot":"","sources":["../../../../../src/components/form/engine/variants/variantModel.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qoretechnologies/reqraft",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.58-pr.129.g4b46b32",
|
|
4
4
|
"description": "ReQraft is a collection of React components and hooks that are used across Qore Technologies' products made using the ReQore component library from Qore.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -75,7 +75,7 @@ const StyledMeter = styled.div<{
|
|
|
75
75
|
background: ${({ $att_c }) => $att_c};
|
|
76
76
|
}
|
|
77
77
|
`;
|
|
78
|
-
// One shared text size for the whole summary line, so "
|
|
78
|
+
// One shared text size for the whole summary line, so "Incomplete", "1/6 set" and
|
|
79
79
|
// "N need attention" all read at the same scale (no chips, no size jumps).
|
|
80
80
|
const StyledSummary = styled.span<{ $color?: string }>`
|
|
81
81
|
font-size: 13px;
|
|
@@ -164,8 +164,13 @@ export const CompactToolbar = memo((reqoreProps: Partial<IReqoreControlGroupProp
|
|
|
164
164
|
{/* "Ready" is a claim about the whole form, so anything the form
|
|
165
165
|
is asking for withdraws it — a value that fails validation and
|
|
166
166
|
a field the host has flagged alike. Saying Ready beside
|
|
167
|
-
"1 need attention" made the word mean nothing.
|
|
168
|
-
|
|
167
|
+
"1 need attention" made the word mean nothing.
|
|
168
|
+
|
|
169
|
+
The other state is "Incomplete", not "Draft": it is about
|
|
170
|
+
what the form still lacks, and a form nobody has touched can
|
|
171
|
+
lack things. "Draft" means unsaved changes and belongs to the
|
|
172
|
+
batched mode's per-row chip alone. */}
|
|
173
|
+
{invalidCount || attentionCount ? 'Incomplete' : 'Ready'}
|
|
169
174
|
</StyledSummary>
|
|
170
175
|
<StyledSummary style={{ opacity: 0.5 }}>
|
|
171
176
|
· {completion.set}/{completion.total} set
|
|
@@ -2153,6 +2153,114 @@ export const CompactWithholdsFieldsWithUnmetDependencies: Story = {
|
|
|
2153
2153
|
},
|
|
2154
2154
|
};
|
|
2155
2155
|
|
|
2156
|
+
/** A check whose kind decides which of the rest apply — the guided check's shape. */
|
|
2157
|
+
const lockedRequiredCheckOptions = {
|
|
2158
|
+
kind: {
|
|
2159
|
+
type: 'string',
|
|
2160
|
+
ui_type: 'string',
|
|
2161
|
+
display_name: 'Kind',
|
|
2162
|
+
short_desc: 'What the check compares',
|
|
2163
|
+
preselected: true,
|
|
2164
|
+
allowed_values: [
|
|
2165
|
+
{ value: { type: 'string', value: 'equals' }, display_name: 'Equals' },
|
|
2166
|
+
{ value: { type: 'string', value: 'between' }, display_name: 'Between' },
|
|
2167
|
+
{ value: { type: 'string', value: 'exists' }, display_name: 'Exists' },
|
|
2168
|
+
],
|
|
2169
|
+
},
|
|
2170
|
+
expected: {
|
|
2171
|
+
type: 'string',
|
|
2172
|
+
ui_type: 'string',
|
|
2173
|
+
display_name: 'Expected value',
|
|
2174
|
+
short_desc: 'Applies once a kind is picked',
|
|
2175
|
+
required: true,
|
|
2176
|
+
depends_on: ['kind'],
|
|
2177
|
+
},
|
|
2178
|
+
min: {
|
|
2179
|
+
type: 'int',
|
|
2180
|
+
ui_type: 'int',
|
|
2181
|
+
display_name: 'Minimum',
|
|
2182
|
+
short_desc: 'Applies to Between alone',
|
|
2183
|
+
required: true,
|
|
2184
|
+
depends_on: ['kind=between'],
|
|
2185
|
+
},
|
|
2186
|
+
max: {
|
|
2187
|
+
type: 'int',
|
|
2188
|
+
ui_type: 'int',
|
|
2189
|
+
display_name: 'Maximum',
|
|
2190
|
+
short_desc: 'Applies to Between alone',
|
|
2191
|
+
required: true,
|
|
2192
|
+
depends_on: ['kind=between'],
|
|
2193
|
+
},
|
|
2194
|
+
} as IOptionsSchema;
|
|
2195
|
+
|
|
2196
|
+
export const CompactLockedRequiredNeedsNoAttention: Story = {
|
|
2197
|
+
parameters: {
|
|
2198
|
+
docs: {
|
|
2199
|
+
description: {
|
|
2200
|
+
story:
|
|
2201
|
+
'Renders a check whose Expected value, Minimum and Maximum are required but locked until a kind is picked. A locked field is not listed under "Needs attention": the author can do nothing with it yet. Each stays on the form, locked, among the optional fields, saying what unlocks it. With no kind picked, nothing needs attention and the header reads Ready: the locked fields do not apply yet.',
|
|
2202
|
+
},
|
|
2203
|
+
},
|
|
2204
|
+
},
|
|
2205
|
+
args: {
|
|
2206
|
+
compact: true,
|
|
2207
|
+
minColumnWidth: '300px',
|
|
2208
|
+
options: lockedRequiredCheckOptions,
|
|
2209
|
+
value: {} as IOptions,
|
|
2210
|
+
},
|
|
2211
|
+
play: async () => {
|
|
2212
|
+
await _testsWaitForText('Expected value');
|
|
2213
|
+
await waitFor(
|
|
2214
|
+
() =>
|
|
2215
|
+
expect(
|
|
2216
|
+
document.querySelector(
|
|
2217
|
+
'.readfirst-row[data-field="expected"] .options-readfirst-lock-deps'
|
|
2218
|
+
)
|
|
2219
|
+
).toBeTruthy(),
|
|
2220
|
+
{ timeout: 10000 }
|
|
2221
|
+
);
|
|
2222
|
+
await expect(document.querySelector('[data-field="min"]')).toBeTruthy();
|
|
2223
|
+
await expect(document.querySelector('[data-field="max"]')).toBeTruthy();
|
|
2224
|
+
await _testsWaitForTextToNotExist('Needs attention');
|
|
2225
|
+
// Nothing the author can fill in is missing, so the form is Ready — the
|
|
2226
|
+
// locked fields do not apply until a kind is picked.
|
|
2227
|
+
await _testsWaitForText('Ready');
|
|
2228
|
+
await _testsWaitForTextToNotExist('Incomplete');
|
|
2229
|
+
},
|
|
2230
|
+
};
|
|
2231
|
+
|
|
2232
|
+
export const CompactUnlockedRequiredNeedsAttention: Story = {
|
|
2233
|
+
parameters: {
|
|
2234
|
+
docs: {
|
|
2235
|
+
description: {
|
|
2236
|
+
story:
|
|
2237
|
+
'Renders the same check with the kind set to Between. Every field that kind unlocks is required and empty, so Expected value, Minimum and Maximum are all listed under "Needs attention" and the header reads Incomplete (never Draft: nothing has been changed).',
|
|
2238
|
+
},
|
|
2239
|
+
},
|
|
2240
|
+
},
|
|
2241
|
+
args: {
|
|
2242
|
+
compact: true,
|
|
2243
|
+
minColumnWidth: '300px',
|
|
2244
|
+
options: lockedRequiredCheckOptions,
|
|
2245
|
+
value: { kind: { type: 'string', value: 'between' } } as IOptions,
|
|
2246
|
+
},
|
|
2247
|
+
play: async () => {
|
|
2248
|
+
await _testsWaitForText('Needs attention');
|
|
2249
|
+
await _testsWaitForText('Incomplete');
|
|
2250
|
+
await _testsWaitForTextToNotExist('Draft');
|
|
2251
|
+
const attention = [...document.querySelectorAll('.options-readfirst-group')].find((group) =>
|
|
2252
|
+
(group.textContent || '').startsWith('Needs attention')
|
|
2253
|
+
);
|
|
2254
|
+
await expect(attention).toBeTruthy();
|
|
2255
|
+
for (const field of ['expected', 'min', 'max']) {
|
|
2256
|
+
await waitFor(
|
|
2257
|
+
() => expect(attention!.querySelector(`[data-field="${field}"]`)).toBeTruthy(),
|
|
2258
|
+
{ timeout: 10000 }
|
|
2259
|
+
);
|
|
2260
|
+
}
|
|
2261
|
+
},
|
|
2262
|
+
};
|
|
2263
|
+
|
|
2156
2264
|
export const CompactConditionalMessage: Story = {
|
|
2157
2265
|
parameters: {
|
|
2158
2266
|
docs: {
|
|
@@ -2616,6 +2724,21 @@ const _expandOptionalBox = async () => {
|
|
|
2616
2724
|
}
|
|
2617
2725
|
};
|
|
2618
2726
|
|
|
2727
|
+
/**
|
|
2728
|
+
* Put one row of the Optional box on screen: open the box, and its "N more
|
|
2729
|
+
* optional fields" fold if the row is behind it. A required field locked by an
|
|
2730
|
+
* unmet dependency waits here rather than under "Needs attention", and a box
|
|
2731
|
+
* that shows its preselected rows folds the rest.
|
|
2732
|
+
*/
|
|
2733
|
+
const _revealOptionalRow = async (field: string) => {
|
|
2734
|
+
await _expandOptionalBox();
|
|
2735
|
+
const row = () => document.querySelector(`.readfirst-row[data-field="${field}"]`);
|
|
2736
|
+
if (!row()) {
|
|
2737
|
+
await _testsClickButton({ selector: '.options-readfirst-more' });
|
|
2738
|
+
}
|
|
2739
|
+
await waitFor(() => expect(row()).toBeTruthy(), { timeout: 10000 });
|
|
2740
|
+
};
|
|
2741
|
+
|
|
2619
2742
|
// CompactSchema plus one optional (non-preselected) field, to exercise the
|
|
2620
2743
|
// "Fields" menu add / select-all / reset actions.
|
|
2621
2744
|
const CompactFieldsMenuSchema: Record<string, TCompactField> = {
|
|
@@ -2785,9 +2908,10 @@ export const CompactReadOnly: Story = {
|
|
|
2785
2908
|
},
|
|
2786
2909
|
play: async () => {
|
|
2787
2910
|
await _testsWaitForText('order-fulfilment');
|
|
2788
|
-
// Nothing that reports progress: no meter, no
|
|
2911
|
+
// Nothing that reports progress: no meter, no Incomplete/Ready, no boxes.
|
|
2789
2912
|
expect(document.querySelector('.options-readfirst-completion')).toBeNull();
|
|
2790
2913
|
expect(document.querySelector('.options-readfirst-group')).toBeNull();
|
|
2914
|
+
await _testsWaitForTextToNotExist('Incomplete');
|
|
2791
2915
|
await _testsWaitForTextToNotExist('Draft');
|
|
2792
2916
|
await _testsWaitForTextToNotExist('Needs attention');
|
|
2793
2917
|
await _testsWaitForTextToNotExist('Optional');
|
|
@@ -2924,9 +3048,10 @@ export const CompactBasic: Story = {
|
|
|
2924
3048
|
value: basicFormValue,
|
|
2925
3049
|
},
|
|
2926
3050
|
play: async () => {
|
|
2927
|
-
// Unresolved required/invalid fields → the header
|
|
2928
|
-
//
|
|
2929
|
-
await _testsWaitForText('
|
|
3051
|
+
// Unresolved required/invalid fields → the header says Incomplete. Never
|
|
3052
|
+
// "Draft": nobody has changed this form, and Draft means unsaved changes.
|
|
3053
|
+
await _testsWaitForText('Incomplete');
|
|
3054
|
+
await _testsWaitForTextToNotExist('Draft');
|
|
2930
3055
|
// Several asserted/clicked fields (Disabled option, …) are empty optionals in
|
|
2931
3056
|
// the collapsed Optional box — open it so they're on screen.
|
|
2932
3057
|
await _expandOptionalBox();
|
|
@@ -2955,7 +3080,9 @@ export const CompactBasic: Story = {
|
|
|
2955
3080
|
).toBeFalsy();
|
|
2956
3081
|
|
|
2957
3082
|
// Dependency locks are navigable: the lock's popover lists the blockers
|
|
2958
|
-
// with their state; clicking one scrolls to + flashes it.
|
|
3083
|
+
// with their state; clicking one scrolls to + flashes it. The locked row is
|
|
3084
|
+
// required, but locked it needs no attention, so it waits in Optional.
|
|
3085
|
+
await _revealOptionalRow('optionWithShortDescription');
|
|
2959
3086
|
const depLock = document.querySelector(
|
|
2960
3087
|
'.readfirst-row[data-field="optionWithShortDescription"] .options-readfirst-lock-deps'
|
|
2961
3088
|
) as HTMLElement;
|
|
@@ -3612,7 +3739,7 @@ export const CompactSensitive: Story = {
|
|
|
3612
3739
|
};
|
|
3613
3740
|
|
|
3614
3741
|
// `rules: ['valid_identifier']` flows from the schema into validation: a bad
|
|
3615
|
-
// identifier marks the form invalid (banner +
|
|
3742
|
+
// identifier marks the form invalid (banner + Incomplete status).
|
|
3616
3743
|
export const CompactValidIdentifierRule: Story = {
|
|
3617
3744
|
parameters: {
|
|
3618
3745
|
docs: {
|
|
@@ -3641,7 +3768,7 @@ export const CompactValidIdentifierRule: Story = {
|
|
|
3641
3768
|
await _testsWaitForText('1-bad-identifier');
|
|
3642
3769
|
// The rules-driven validation marks the form as needing attention — the
|
|
3643
3770
|
// dedicated "Needs attention" box (and the header link) signal it.
|
|
3644
|
-
await _testsWaitForText('
|
|
3771
|
+
await _testsWaitForText('Incomplete');
|
|
3645
3772
|
await _testsWaitForText('Needs attention');
|
|
3646
3773
|
},
|
|
3647
3774
|
};
|
|
@@ -5801,12 +5928,12 @@ export const CompactRequiredGroups: Story = {
|
|
|
5801
5928
|
value: {} as IOptions,
|
|
5802
5929
|
},
|
|
5803
5930
|
play: async () => {
|
|
5804
|
-
// All three members show the required placeholder + the
|
|
5931
|
+
// All three members show the required placeholder + the Incomplete status. The two
|
|
5805
5932
|
// contiguous members (byHost/byFile in Connection) cluster into a rail, which
|
|
5806
5933
|
// carries the grouping in place of a chip; only the lone member (byUrl in
|
|
5807
5934
|
// General) keeps a "One of" chip — so exactly one chip, not three.
|
|
5808
5935
|
await _testsWaitForTextsCount('—', undefined, 3);
|
|
5809
|
-
await _testsWaitForText('
|
|
5936
|
+
await _testsWaitForText('Incomplete');
|
|
5810
5937
|
await _testsWaitForTextsCount('One of', undefined, 1);
|
|
5811
5938
|
|
|
5812
5939
|
// The chip is a ReqoreDropdown listing the siblings; selecting one flashes
|
|
@@ -5861,7 +5988,7 @@ export const CompactRequiredGroups: Story = {
|
|
|
5861
5988
|
await _testsClickButton({ selector: '.options-readfirst-done' });
|
|
5862
5989
|
await _testsWaitForText('https://example.com');
|
|
5863
5990
|
|
|
5864
|
-
// One fulfilled member satisfies the group → the
|
|
5991
|
+
// One fulfilled member satisfies the group → the status flips to Ready and the
|
|
5865
5992
|
// Once satisfied: the filled member keeps a "Covers" chip; the empty siblings
|
|
5866
5993
|
// show their "Covered by 'By URL'" note INLINE (not a chip), and no "One of"
|
|
5867
5994
|
// remains. So exactly one required-group chip stays (the coverer's).
|
|
@@ -6468,7 +6595,9 @@ export const CompactShowcase: Story = {
|
|
|
6468
6595
|
await expect(document.querySelectorAll('.options-readfirst-info-panel').length).toBeGreaterThan(
|
|
6469
6596
|
0
|
|
6470
6597
|
);
|
|
6471
|
-
// The unmet-dependency hint surfaces on the dependent field's row
|
|
6598
|
+
// The unmet-dependency hint surfaces on the dependent field's row — locked,
|
|
6599
|
+
// so in the Optional box rather than under "Needs attention".
|
|
6600
|
+
await _revealOptionalRow('optionWithShortDescription');
|
|
6472
6601
|
await _testsWaitForText(
|
|
6473
6602
|
'This field is disabled because some dependencies are not fulfilled: "basicOption"'
|
|
6474
6603
|
);
|
|
@@ -2324,10 +2324,13 @@ const FormEngineImpl = ({
|
|
|
2324
2324
|
|
|
2325
2325
|
const isOptionValid = useCallback(
|
|
2326
2326
|
(optionName: string, type: TQorusType, optionValue: any) => {
|
|
2327
|
+
const isEmpty = optionValue === undefined || optionValue === '';
|
|
2328
|
+
// An empty field locked by an unmet `depends_on` does not apply, so it is
|
|
2329
|
+
// not missing — the same rule the read-first status follows.
|
|
2327
2330
|
if (
|
|
2328
|
-
|
|
2329
|
-
!options?.[optionName]?.required_groups
|
|
2330
|
-
|
|
2331
|
+
isEmpty &&
|
|
2332
|
+
((!options?.[optionName]?.required && !options?.[optionName]?.required_groups) ||
|
|
2333
|
+
dependencyLockedNames.includes(optionName))
|
|
2331
2334
|
) {
|
|
2332
2335
|
return true;
|
|
2333
2336
|
}
|
|
@@ -2342,7 +2345,12 @@ const FormEngineImpl = ({
|
|
|
2342
2345
|
isFunction: (availableOptions?.[optionName] as { is_expression?: boolean })?.is_expression,
|
|
2343
2346
|
} as any);
|
|
2344
2347
|
},
|
|
2345
|
-
[
|
|
2348
|
+
[
|
|
2349
|
+
JSON.stringify(options),
|
|
2350
|
+
JSON.stringify(availableOptions),
|
|
2351
|
+
JSON.stringify(localValue.fields),
|
|
2352
|
+
dependencyLockedNames,
|
|
2353
|
+
]
|
|
2346
2354
|
);
|
|
2347
2355
|
|
|
2348
2356
|
const getValidityData = useCallback((): IFormValidityData => {
|
|
@@ -2366,7 +2374,17 @@ const FormEngineImpl = ({
|
|
|
2366
2374
|
|
|
2367
2375
|
let validation: IValidationResult;
|
|
2368
2376
|
|
|
2369
|
-
|
|
2377
|
+
/* A required field locked by an unmet `depends_on` does not apply
|
|
2378
|
+
while it is locked: it is not in "Needs attention", and an empty one
|
|
2379
|
+
does not make the form incomplete either. Otherwise a check whose
|
|
2380
|
+
kind is Equals could never be saved, because Minimum — required for
|
|
2381
|
+
Between alone — is empty; and a form whose only gaps are fields
|
|
2382
|
+
nobody can fill in would say Incomplete with nothing to act on. A
|
|
2383
|
+
locked field that still HOLDS a value is validated as before. */
|
|
2384
|
+
if (
|
|
2385
|
+
isEmpty &&
|
|
2386
|
+
((!isRequired && !hasRequiredGroups) || dependencyLockedNames.includes(optionName))
|
|
2387
|
+
) {
|
|
2370
2388
|
validation = { isValid: true, reasons: [] };
|
|
2371
2389
|
} else {
|
|
2372
2390
|
validation = validateFieldWithResult(getType(type), optionValue, {
|
|
@@ -2404,6 +2422,7 @@ const FormEngineImpl = ({
|
|
|
2404
2422
|
JSON.stringify(options),
|
|
2405
2423
|
JSON.stringify(operators),
|
|
2406
2424
|
JSON.stringify(localValue.fields),
|
|
2425
|
+
dependencyLockedNames,
|
|
2407
2426
|
]);
|
|
2408
2427
|
|
|
2409
2428
|
const validityData = useMemo(() => getValidityData(), [getValidityData]);
|
|
@@ -2487,7 +2506,14 @@ const FormEngineImpl = ({
|
|
|
2487
2506
|
// sub-form it summarises said 0/3 set.
|
|
2488
2507
|
const empty = isOptionValueEmpty(value) || isUnsetSchemaHash(value, schema);
|
|
2489
2508
|
const reqGroups = (schema?.required_groups as string[] | undefined) || [];
|
|
2490
|
-
|
|
2509
|
+
// Required only while it applies. A field locked by an unmet `depends_on`
|
|
2510
|
+
// renders disabled with what unlocks it; nothing the author can do to it
|
|
2511
|
+
// is a next step — the next step is the field it depends on, which carries
|
|
2512
|
+
// its own status. Counting it here put a guided check's locked Path /
|
|
2513
|
+
// Minimum / Maximum under "Needs attention" before its kind was even
|
|
2514
|
+
// picked. The moment the dependency holds, it is required again.
|
|
2515
|
+
const required =
|
|
2516
|
+
!dependencyLockedNames.includes(name) && !!(schema?.required || reqGroups.length);
|
|
2491
2517
|
const covered =
|
|
2492
2518
|
empty &&
|
|
2493
2519
|
reqGroups.some((g) => {
|
|
@@ -2512,11 +2538,15 @@ const FormEngineImpl = ({
|
|
|
2512
2538
|
isOptionValid,
|
|
2513
2539
|
requiredGroupsInfo,
|
|
2514
2540
|
schemaMsgIntent,
|
|
2541
|
+
dependencyLockedNames,
|
|
2515
2542
|
]
|
|
2516
2543
|
);
|
|
2517
2544
|
const getOptionBucket = useCallback(
|
|
2518
2545
|
(name: string, hidden = false): 'attention' | 'set' | 'optional' => {
|
|
2519
|
-
|
|
2546
|
+
// A locked one-of member cannot be the one that satisfies its group, so it
|
|
2547
|
+
// does not travel with the group into "Needs attention"; its own status
|
|
2548
|
+
// (not required while locked) decides its box.
|
|
2549
|
+
if (!hidden && !dependencyLockedNames.includes(name)) {
|
|
2520
2550
|
const reqGroups = (options?.[name]?.required_groups as string[] | undefined) || [];
|
|
2521
2551
|
if (reqGroups.length) {
|
|
2522
2552
|
return reqGroups.some((g) => !requiredGroupsInfo.satisfiedBy[g]) ? 'attention' : 'set';
|
|
@@ -2524,7 +2554,7 @@ const FormEngineImpl = ({
|
|
|
2524
2554
|
}
|
|
2525
2555
|
return getReadFirstBucket(getOptionStatus(name, hidden));
|
|
2526
2556
|
},
|
|
2527
|
-
[JSON.stringify(options), requiredGroupsInfo, getOptionStatus]
|
|
2557
|
+
[JSON.stringify(options), requiredGroupsInfo, getOptionStatus, dependencyLockedNames]
|
|
2528
2558
|
);
|
|
2529
2559
|
// How many fields are in the "Needs attention" box — drives the header link.
|
|
2530
2560
|
const readFirstAttentionCount = useMemo(
|
|
@@ -358,6 +358,9 @@ export const getOptionFieldMessages = ({
|
|
|
358
358
|
}: IOptionFieldMessagesProps): IReqoreTagProps[] => {
|
|
359
359
|
const optionSchema = schema[name];
|
|
360
360
|
const result: IReqoreTagProps[] = [];
|
|
361
|
+
const locked =
|
|
362
|
+
!!optionSchema?.depends_on &&
|
|
363
|
+
!hasAllDependenciesFullfilled(optionSchema.depends_on, allOptions, schema);
|
|
361
364
|
|
|
362
365
|
if (option.value || option.value === false || option.value === 0 || option.value === null) {
|
|
363
366
|
/* A concrete type the VALUE carries beats an untyped schema.
|
|
@@ -392,7 +395,9 @@ export const getOptionFieldMessages = ({
|
|
|
392
395
|
});
|
|
393
396
|
}
|
|
394
397
|
} else {
|
|
395
|
-
|
|
398
|
+
// A locked field is not asked for: the dependency message below says what
|
|
399
|
+
// unlocks it, and until then there is nothing to fill in.
|
|
400
|
+
if (optionSchema?.required && !untouched && !locked) {
|
|
396
401
|
result.push({ label: 'This field is required', intent: 'danger' });
|
|
397
402
|
}
|
|
398
403
|
|
|
@@ -413,10 +418,7 @@ export const getOptionFieldMessages = ({
|
|
|
413
418
|
}
|
|
414
419
|
}
|
|
415
420
|
|
|
416
|
-
if (
|
|
417
|
-
optionSchema?.depends_on &&
|
|
418
|
-
!hasAllDependenciesFullfilled(optionSchema.depends_on, allOptions, schema)
|
|
419
|
-
) {
|
|
421
|
+
if (locked) {
|
|
420
422
|
const dependsOn = describeDependencies(optionSchema.depends_on, schema);
|
|
421
423
|
|
|
422
424
|
result.push({
|
|
@@ -112,7 +112,9 @@ const Meter = ({ form }: { form: TVariantForm }) => {
|
|
|
112
112
|
color: (s.attention ? c.warning : c.success) as never,
|
|
113
113
|
}}
|
|
114
114
|
>
|
|
115
|
-
{
|
|
115
|
+
{/* What the form lacks, not whether it has unsaved changes:
|
|
116
|
+
"Draft" is the batched mode's per-row chip alone. */}
|
|
117
|
+
{s.attention ? 'Incomplete' : 'Ready'}
|
|
116
118
|
</ReqoreP>
|
|
117
119
|
<span>
|
|
118
120
|
{s.set} of {s.total} set
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
isOptionValueEmpty,
|
|
21
21
|
} from '../readFirst';
|
|
22
22
|
import { isValueTemplate } from '../../../../helpers/templates';
|
|
23
|
+
import { hasAllDependenciesFullfilled } from '../../../../helpers/validations';
|
|
23
24
|
|
|
24
25
|
export type TVariantStatus =
|
|
25
26
|
| 'set' // has a valid value
|
|
@@ -153,8 +154,13 @@ export function buildVariantGroups(
|
|
|
153
154
|
const empty = isOptionValueEmpty(field?.value);
|
|
154
155
|
const ownGroups: string[] = schema?.required_groups || [];
|
|
155
156
|
// Member of a one-of group that nobody has satisfied yet.
|
|
156
|
-
|
|
157
|
-
|
|
157
|
+
// A field locked by an unmet `depends_on` does not apply yet, so it is
|
|
158
|
+
// neither required nor part of an unmet group — the engine's rule.
|
|
159
|
+
const locked =
|
|
160
|
+
!!schema?.depends_on && !hasAllDependenciesFullfilled(schema.depends_on, values, options);
|
|
161
|
+
const inUnmetGroup =
|
|
162
|
+
!locked && ownGroups.some((k) => reqGroups[k] && !reqGroups[k].satisfied);
|
|
163
|
+
const required = !locked && !!schema?.required;
|
|
158
164
|
|
|
159
165
|
let status: TVariantStatus;
|
|
160
166
|
let reason: string | undefined;
|
|
@@ -183,7 +189,8 @@ export function buildVariantGroups(
|
|
|
183
189
|
label: (schema?.display_name as string) || name,
|
|
184
190
|
shortDesc: schema?.short_desc,
|
|
185
191
|
longDesc: schema?.desc,
|
|
186
|
-
|
|
192
|
+
// The asterisk still marks it: it is required once it applies.
|
|
193
|
+
required: !!schema?.required || ownGroups.length > 0,
|
|
187
194
|
readOnly: !!schema?.readonly,
|
|
188
195
|
status,
|
|
189
196
|
reason,
|