@qoretechnologies/reqraft 0.10.66-pr.145.g620503a → 0.10.66-pr.145.g7df2015
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/dist/components/form/engine/FormEngine.d.ts +9 -9
- package/dist/components/form/engine/formAppearance.d.ts +10 -11
- package/dist/components/form/engine/formAppearance.d.ts.map +1 -1
- package/dist/components/form/engine/formAppearance.js +2 -2
- package/dist/components/form/engine/formAppearance.js.map +1 -1
- package/package.json +1 -1
- package/src/components/form/engine/FormEngine.stories.tsx +25 -27
- package/src/components/form/engine/FormEngine.tsx +8 -8
- package/src/components/form/engine/FormEngineAppearance.stories.tsx +15 -8
- package/src/components/form/engine/formAppearance.ts +11 -12
|
@@ -286,15 +286,15 @@ export interface IFormEngineProps extends Omit<IReqoreCollectionProps, 'onChange
|
|
|
286
286
|
*/
|
|
287
287
|
compact?: boolean;
|
|
288
288
|
/**
|
|
289
|
-
* How the form LOOKS — never what it does. `'
|
|
290
|
-
* status boxes as plain panels on the theme's panel
|
|
291
|
-
* rows split by hairlines, grey labels, an amber
|
|
292
|
-
* place of red asterisks and messages, quiet
|
|
293
|
-
* Yes/No as words, and a readiness meter
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
*
|
|
289
|
+
* How the form LOOKS — never what it does. `'default'` keeps the established
|
|
290
|
+
* look; `'calm'` draws the status boxes as plain panels on the theme's panel
|
|
291
|
+
* surface with label/value rows split by hairlines, grey labels, an amber
|
|
292
|
+
* "Required · add a value" in place of red asterisks and messages, quiet
|
|
293
|
+
* counts, no dot on a healthy row, Yes/No as words, and a readiness meter
|
|
294
|
+
* whose done part is grey. See {@link TFormEngineAppearance}.
|
|
295
|
+
*
|
|
296
|
+
* Opt-in, so a consumer that does not pass it is unchanged. Inherited: a
|
|
297
|
+
* nested (`arg_schema`) sub-form and every field editor the form mounts
|
|
298
298
|
* follow the form's appearance, and a form with no `appearance` of its own
|
|
299
299
|
* follows a `FormAppearanceProvider` above it. Applies to compact mode; the
|
|
300
300
|
* classic collection layout only passes it on to its field editors.
|
|
@@ -4,17 +4,16 @@ import { IReqoreTheme } from '@qoretechnologies/reqore/dist/constants/theme';
|
|
|
4
4
|
* How a form looks. The look only: which fields exist, how they bucket, what a
|
|
5
5
|
* click does and what is validated are identical in every appearance.
|
|
6
6
|
*
|
|
7
|
-
* - `'
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* every row.
|
|
7
|
+
* - `'default'` — the established read-first look: tinted status boxes, an
|
|
8
|
+
* intent-coloured completion meter, a status dot on every row.
|
|
9
|
+
* - `'calm'` — colour only where something needs a person. The status boxes are
|
|
10
|
+
* plain panels on the theme's panel surface with label/value rows split by
|
|
11
|
+
* hairlines; labels are grey; a missing required value is an amber word
|
|
12
|
+
* instead of a red asterisk and message; counts are quiet numbers; a healthy
|
|
13
|
+
* row carries no dot; Yes/No are plain words; nothing is forced into
|
|
14
|
+
* uppercase. Opt-in, so an app that has not asked for it does not change.
|
|
16
15
|
*/
|
|
17
|
-
export type TFormEngineAppearance = '
|
|
16
|
+
export type TFormEngineAppearance = 'default' | 'calm';
|
|
18
17
|
/**
|
|
19
18
|
* The appearance in force for everything below it: the engine's own rows and
|
|
20
19
|
* toolbar, every field editor it mounts, and every nested (`arg_schema`)
|
|
@@ -26,7 +25,7 @@ export type TFormEngineAppearance = 'calm' | 'classic';
|
|
|
26
25
|
*/
|
|
27
26
|
export declare const FormAppearanceContext: import("react").Context<TFormEngineAppearance>;
|
|
28
27
|
export declare const FormAppearanceProvider: import("react").Provider<TFormEngineAppearance>;
|
|
29
|
-
/** The appearance in force here; `'
|
|
28
|
+
/** The appearance in force here; `'default'` when nothing set one. */
|
|
30
29
|
export declare const useFormAppearance: () => TFormEngineAppearance;
|
|
31
30
|
/** Shorthand for the one question most call sites ask. */
|
|
32
31
|
export declare const useIsCalmForm: () => boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formAppearance.d.ts","sourceRoot":"","sources":["../../../../src/components/form/engine/formAppearance.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,+CAA+C,CAAC;AAQ7E
|
|
1
|
+
{"version":3,"file":"formAppearance.d.ts","sourceRoot":"","sources":["../../../../src/components/form/engine/formAppearance.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,+CAA+C,CAAC;AAQ7E;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,MAAM,CAAC;AAEvD;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,gDAA8D,CAAC;AAEjG,eAAO,MAAM,sBAAsB,iDAAiC,CAAC;AAErE,sEAAsE;AACtE,eAAO,MAAM,iBAAiB,QAAO,qBACW,CAAC;AAEjD,0DAA0D;AAC1D,eAAO,MAAM,aAAa,QAAO,OAAyC,CAAC;AAiB3E;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,UAAW,YAAY,KAAG,MACmC,CAAC;AAE9F,0EAA0E;AAC1E,eAAO,MAAM,oBAAoB,UAAW,YAAY,KAAG,MACqC,CAAC;AAEjG;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,gCAA2B,CAAC;AAC5D,eAAO,MAAM,wBAAwB,4BAAuB,CAAC"}
|
|
@@ -14,8 +14,8 @@ var react_1 = require("react");
|
|
|
14
14
|
*/
|
|
15
15
|
exports.FormAppearanceContext = (0, react_1.createContext)(undefined);
|
|
16
16
|
exports.FormAppearanceProvider = exports.FormAppearanceContext.Provider;
|
|
17
|
-
/** The appearance in force here; `'
|
|
18
|
-
var useFormAppearance = function () { var _a; return (_a = (0, react_1.useContext)(exports.FormAppearanceContext)) !== null && _a !== void 0 ? _a : '
|
|
17
|
+
/** The appearance in force here; `'default'` when nothing set one. */
|
|
18
|
+
var useFormAppearance = function () { var _a; return (_a = (0, react_1.useContext)(exports.FormAppearanceContext)) !== null && _a !== void 0 ? _a : 'default'; };
|
|
19
19
|
exports.useFormAppearance = useFormAppearance;
|
|
20
20
|
/** Shorthand for the one question most call sites ask. */
|
|
21
21
|
var useIsCalmForm = function () { return (0, exports.useFormAppearance)() === 'calm'; };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formAppearance.js","sourceRoot":"","sources":["../../../../src/components/form/engine/formAppearance.ts"],"names":[],"mappings":";;;AACA,uEAIsD;AACtD,+BAAkD;
|
|
1
|
+
{"version":3,"file":"formAppearance.js","sourceRoot":"","sources":["../../../../src/components/form/engine/formAppearance.ts"],"names":[],"mappings":";;;AACA,uEAIsD;AACtD,+BAAkD;AAiBlD;;;;;;;;GAQG;AACU,QAAA,qBAAqB,GAAG,IAAA,qBAAa,EAAoC,SAAS,CAAC,CAAC;AAEpF,QAAA,sBAAsB,GAAG,6BAAqB,CAAC,QAAQ,CAAC;AAErE,sEAAsE;AAC/D,IAAM,iBAAiB,GAAG,sBAC/B,OAAA,MAAA,IAAA,kBAAU,EAAC,6BAAqB,CAAC,mCAAI,SAAS,CAAA,EAAA,CAAC;AADpC,QAAA,iBAAiB,qBACmB;AAEjD,0DAA0D;AACnD,IAAM,aAAa,GAAG,cAAe,OAAA,IAAA,yBAAiB,GAAE,KAAK,MAAM,EAA9B,CAA8B,CAAC;AAA9D,QAAA,aAAa,iBAAiD;AAE3E,8EAA8E;AAC9E,IAAM,uBAAuB,GAAG,UAC9B,KAAmB;IAEnB,IAAM,MAAM,GAAG,KAGd,CAAC;IACF,6EAA6E;IAC7E,6EAA6E;IAC7E,OAAO,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;QAClF,MAAM,CAAC,KAAK;QACd,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC,CAAC;AAEF;;;;;GAKG;AACI,IAAM,mBAAmB,GAAG,UAAC,KAAmB,gBACrD,OAAA,MAAA,MAAA,uBAAuB,CAAC,KAAK,CAAC,0CAAE,IAAI,mCAAI,IAAA,uBAAc,EAAC,IAAA,+BAAsB,EAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAA,EAAA,CAAC;AADjF,QAAA,mBAAmB,uBAC8D;AAE9F,0EAA0E;AACnE,IAAM,oBAAoB,GAAG,UAAC,KAAmB,gBACtD,OAAA,MAAA,MAAA,uBAAuB,CAAC,KAAK,CAAC,0CAAE,MAAM,mCAAI,IAAA,wBAAe,EAAC,IAAA,+BAAsB,EAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAA,EAAA,CAAC;AADpF,QAAA,oBAAoB,wBACgE;AAEjG;;;;GAIG;AACU,QAAA,mBAAmB,GAAG,wBAAwB,CAAC;AAC/C,QAAA,wBAAwB,GAAG,oBAAoB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qoretechnologies/reqraft",
|
|
3
|
-
"version": "0.10.66-pr.145.
|
|
3
|
+
"version": "0.10.66-pr.145.g7df2015",
|
|
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",
|
|
@@ -39,7 +39,6 @@ import {
|
|
|
39
39
|
IOptionsSchemaArg,
|
|
40
40
|
} from './FormEngine';
|
|
41
41
|
import { basicFormValue, getBasicFormOptions as getOptions } from './__fixtures__/basicFormOptions';
|
|
42
|
-
import { CALM_REQUIRED_LABEL, CALM_REQUIRED_READ_LABEL } from './formAppearance';
|
|
43
42
|
import { chromeFieldBases, metaFieldBases } from './__fixtures__/fieldChromeOptions';
|
|
44
43
|
|
|
45
44
|
/** The first status box: "To fill in" while nothing in it is a fault (a new form),
|
|
@@ -632,7 +631,7 @@ export const OptionDependsOnOptionInRequiredGroup: Story = {
|
|
|
632
631
|
await waitFor(() =>
|
|
633
632
|
expect(
|
|
634
633
|
[...document.querySelectorAll('.system-option')].some(
|
|
635
|
-
(row) => row.textContent?.includes('Required Option 6') && row.textContent?.includes(
|
|
634
|
+
(row) => row.textContent?.includes('Required Option 6') && row.textContent?.includes('This field is required')
|
|
636
635
|
)
|
|
637
636
|
).toBe(true)
|
|
638
637
|
);
|
|
@@ -2851,8 +2850,7 @@ export const Compact: Story = {
|
|
|
2851
2850
|
await _testsWaitForText('order-fulfilment');
|
|
2852
2851
|
await _testsWaitForText('orders, batch');
|
|
2853
2852
|
await _testsWaitForText('Yes');
|
|
2854
|
-
|
|
2855
|
-
await _testsWaitForText(CALM_REQUIRED_LABEL);
|
|
2853
|
+
await _testsWaitForText('—');
|
|
2856
2854
|
},
|
|
2857
2855
|
};
|
|
2858
2856
|
|
|
@@ -3174,7 +3172,7 @@ export const CompactEmpty: Story = {
|
|
|
3174
3172
|
docs: {
|
|
3175
3173
|
description: {
|
|
3176
3174
|
story:
|
|
3177
|
-
'Renders the Compact fixture with no value —
|
|
3175
|
+
'Renders the Compact fixture with no value — all six empty fields render a dash placeholder and the four optional fields sit in the collapsed Optional box.',
|
|
3178
3176
|
},
|
|
3179
3177
|
},
|
|
3180
3178
|
},
|
|
@@ -3189,9 +3187,9 @@ export const CompactEmpty: Story = {
|
|
|
3189
3187
|
// The four empty OPTIONAL fields live in the (collapsed) Optional box — open
|
|
3190
3188
|
// it so all six empty fields are on screen.
|
|
3191
3189
|
await _expandOptionalBox();
|
|
3192
|
-
// Both required fields
|
|
3193
|
-
|
|
3194
|
-
await _testsWaitForTextsCount('—', undefined,
|
|
3190
|
+
// Both required fields read as unset; the four optional ones as "Not set".
|
|
3191
|
+
// All six empty fields read as a calm dash (the red asterisk marks required).
|
|
3192
|
+
await _testsWaitForTextsCount('—', undefined, 6);
|
|
3195
3193
|
},
|
|
3196
3194
|
};
|
|
3197
3195
|
|
|
@@ -3380,7 +3378,7 @@ export const CompactReadOnlyRichText: Story = {
|
|
|
3380
3378
|
// A read view says what is wrong with the record, as an editable one does:
|
|
3381
3379
|
// the fixture's invalid value and its empty required fields say so.
|
|
3382
3380
|
await _testsWaitForText('Text value is empty');
|
|
3383
|
-
await _testsWaitForText(
|
|
3381
|
+
await _testsWaitForText('This field is required');
|
|
3384
3382
|
|
|
3385
3383
|
const row = (name: string) => document.querySelector(`.readfirst-row[data-field="${name}"]`);
|
|
3386
3384
|
// Richtext: the text and its chip, no Slate.
|
|
@@ -3539,7 +3537,7 @@ export const CompactReadOnlyValues: Story = {
|
|
|
3539
3537
|
docs: {
|
|
3540
3538
|
description: {
|
|
3541
3539
|
story:
|
|
3542
|
-
'Read-only as a fact sheet, not a disabled editor: no completion meter, no Needs attention / Set / Optional boxes, no required asterisks; an optional field nobody set is not listed, a required one nobody set
|
|
3540
|
+
'Read-only as a fact sheet, not a disabled editor: no completion meter, no Needs attention / Set / Optional boxes, no required asterisks; an optional field nobody set is not listed, a required one nobody set reads as unset without the amber and says "This field is required"; every value is drawn in full in its row — long text wrapped under a Show more cap, the whole code block, every field of the hash, the chosen option with its description — and no row is a control: nothing is a button, and a click opens nothing.',
|
|
3543
3541
|
},
|
|
3544
3542
|
},
|
|
3545
3543
|
chromatic: { disable: true },
|
|
@@ -3573,7 +3571,7 @@ export const CompactReadOnlyValues: Story = {
|
|
|
3573
3571
|
)
|
|
3574
3572
|
).toBeNull();
|
|
3575
3573
|
expect(document.querySelector('.readfirst-row[data-field="owner"]')?.textContent).toContain(
|
|
3576
|
-
|
|
3574
|
+
'This field is required'
|
|
3577
3575
|
);
|
|
3578
3576
|
|
|
3579
3577
|
const row = (name: string) =>
|
|
@@ -4164,8 +4162,8 @@ export const CompactReadFirstEditing: Story = {
|
|
|
4164
4162
|
// Booleans render as Yes/No, allowed_values as their display label.
|
|
4165
4163
|
await _testsWaitForText('Yes');
|
|
4166
4164
|
await _testsWaitForText('Python');
|
|
4167
|
-
// The required-but-empty field
|
|
4168
|
-
await _testsWaitForText(
|
|
4165
|
+
// The required-but-empty field shows its placeholder instead of an editor.
|
|
4166
|
+
await _testsWaitForText('—');
|
|
4169
4167
|
// No field editor (textarea) is mounted while everything is collapsed.
|
|
4170
4168
|
await expect(document.querySelectorAll('.reqore-textarea')).toHaveLength(0);
|
|
4171
4169
|
|
|
@@ -8553,7 +8551,7 @@ export const CompactMaxFieldsShown: Story = {
|
|
|
8553
8551
|
docs: {
|
|
8554
8552
|
description: {
|
|
8555
8553
|
story:
|
|
8556
|
-
'Renders a compact form with `maxFieldsShown: 3`, so each status box shows three rows and folds the rest behind a
|
|
8554
|
+
'Renders a compact form with `maxFieldsShown: 3`, so each status box shows three rows and folds the rest behind a dashed "N more fields" row. Clicking that row reveals the remainder and the label becomes "Show fewer".',
|
|
8557
8555
|
},
|
|
8558
8556
|
},
|
|
8559
8557
|
},
|
|
@@ -8585,12 +8583,13 @@ export const CompactMaxFieldsShown: Story = {
|
|
|
8585
8583
|
expect(more.classList.contains('readfirst-row')).toBe(true);
|
|
8586
8584
|
expect(more.getAttribute('aria-expanded')).toBe('false');
|
|
8587
8585
|
|
|
8588
|
-
//
|
|
8589
|
-
//
|
|
8586
|
+
// The dashed edge is the thing that says "not a field". Asserting the
|
|
8587
|
+
// COMPUTED style, because the first cut of this took its colour from the
|
|
8588
|
+
// inter-row divider tint (~8% of the text colour) and rendered an invisible
|
|
8589
|
+
// border — the rule was there, the affordance was not.
|
|
8590
8590
|
const edge = getComputedStyle(more);
|
|
8591
|
-
expect(edge.
|
|
8591
|
+
expect(edge.borderStyle).toBe('dashed');
|
|
8592
8592
|
expect(edge.borderTopWidth).toBe('1px');
|
|
8593
|
-
expect(edge.borderBottomStyle).toBe('none');
|
|
8594
8593
|
// …and it inherits the rows' geometry rather than inventing its own
|
|
8595
8594
|
expect(edge.paddingLeft).toBe(
|
|
8596
8595
|
getComputedStyle(
|
|
@@ -9429,7 +9428,7 @@ export const ANewFormSaysWhatIsRequired: Story = {
|
|
|
9429
9428
|
docs: {
|
|
9430
9429
|
description: {
|
|
9431
9430
|
story:
|
|
9432
|
-
'A brand-new form with two empty required fields. Each says "
|
|
9431
|
+
'A brand-new form with two empty required fields. Each says "This field is required" from the start, including the first one, which opens for editing, without waiting for the author to visit it. Saying what is required is not saying something is wrong: the first box reads "To fill in", in the quiet colour, until a field has been visited and left empty.',
|
|
9433
9432
|
},
|
|
9434
9433
|
},
|
|
9435
9434
|
},
|
|
@@ -9446,8 +9445,8 @@ export const ANewFormSaysWhatIsRequired: Story = {
|
|
|
9446
9445
|
await waitFor(() => {
|
|
9447
9446
|
const title = canvasElement.querySelector('[data-field="title"]');
|
|
9448
9447
|
const owner = canvasElement.querySelector('[data-field="owner"]');
|
|
9449
|
-
expect(title?.textContent).toContain(
|
|
9450
|
-
expect(owner?.textContent).toContain(
|
|
9448
|
+
expect(title?.textContent).toContain('This field is required');
|
|
9449
|
+
expect(owner?.textContent).toContain('This field is required');
|
|
9451
9450
|
});
|
|
9452
9451
|
},
|
|
9453
9452
|
};
|
|
@@ -9457,7 +9456,7 @@ export const AReadOnlyFormShowsWhatIsWrong: Story = {
|
|
|
9457
9456
|
docs: {
|
|
9458
9457
|
description: {
|
|
9459
9458
|
story:
|
|
9460
|
-
'A read-only form shows the same validation messages as an editable one: the empty required Title and Owner say "
|
|
9459
|
+
'A read-only form shows the same validation messages as an editable one: the empty required Title and Owner say "This field is required", and Kind says its stored value is not one of the choices. Each empty value slot keeps its plain "—", so each requirement is stated once.',
|
|
9461
9460
|
},
|
|
9462
9461
|
},
|
|
9463
9462
|
},
|
|
@@ -9472,12 +9471,11 @@ export const AReadOnlyFormShowsWhatIsWrong: Story = {
|
|
|
9472
9471
|
await _testsWaitForText('"sideways" is not one of the choices');
|
|
9473
9472
|
await waitFor(() => {
|
|
9474
9473
|
const title = canvasElement.querySelector('[data-field="title"]');
|
|
9475
|
-
expect(title?.textContent
|
|
9476
|
-
expect(title?.
|
|
9477
|
-
|
|
9478
|
-
);
|
|
9474
|
+
expect(title?.textContent).toContain('This field is required');
|
|
9475
|
+
expect(title?.textContent?.split('This field is required')).toHaveLength(2);
|
|
9476
|
+
expect(title?.querySelector('.options-readfirst-valuetext')?.textContent).toBe('—');
|
|
9479
9477
|
expect(canvasElement.querySelector('[data-field="owner"]')?.textContent).toContain(
|
|
9480
|
-
|
|
9478
|
+
'This field is required'
|
|
9481
9479
|
);
|
|
9482
9480
|
});
|
|
9483
9481
|
},
|
|
@@ -871,15 +871,15 @@ export interface IFormEngineProps extends Omit<IReqoreCollectionProps, 'onChange
|
|
|
871
871
|
*/
|
|
872
872
|
compact?: boolean;
|
|
873
873
|
/**
|
|
874
|
-
* How the form LOOKS — never what it does. `'
|
|
875
|
-
* status boxes as plain panels on the theme's panel
|
|
876
|
-
* rows split by hairlines, grey labels, an amber
|
|
877
|
-
* place of red asterisks and messages, quiet
|
|
878
|
-
* Yes/No as words, and a readiness meter
|
|
879
|
-
*
|
|
880
|
-
* host that asks for it. See {@link TFormEngineAppearance}.
|
|
874
|
+
* How the form LOOKS — never what it does. `'default'` keeps the established
|
|
875
|
+
* look; `'calm'` draws the status boxes as plain panels on the theme's panel
|
|
876
|
+
* surface with label/value rows split by hairlines, grey labels, an amber
|
|
877
|
+
* "Required · add a value" in place of red asterisks and messages, quiet
|
|
878
|
+
* counts, no dot on a healthy row, Yes/No as words, and a readiness meter
|
|
879
|
+
* whose done part is grey. See {@link TFormEngineAppearance}.
|
|
881
880
|
*
|
|
882
|
-
*
|
|
881
|
+
* Opt-in, so a consumer that does not pass it is unchanged. Inherited: a
|
|
882
|
+
* nested (`arg_schema`) sub-form and every field editor the form mounts
|
|
883
883
|
* follow the form's appearance, and a form with no `appearance` of its own
|
|
884
884
|
* follows a `FormAppearanceProvider` above it. Applies to compact mode; the
|
|
885
885
|
* classic collection layout only passes it on to its field editors.
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
import { CALM_REQUIRED_LABEL, CALM_REQUIRED_READ_LABEL } from './formAppearance';
|
|
18
18
|
|
|
19
19
|
/*
|
|
20
|
-
* The calm appearance
|
|
20
|
+
* The calm appearance against the default one, over the fields a workflow
|
|
21
21
|
* creator actually serves (display names, groups and descriptions as the Qorus
|
|
22
22
|
* server's workflow metadata gives them), so the comparison is about the look
|
|
23
23
|
* and nothing else.
|
|
@@ -252,21 +252,18 @@ const expectNoDot = (field: string) => {
|
|
|
252
252
|
* Stories
|
|
253
253
|
* ------------------------------------------------------------------------ */
|
|
254
254
|
|
|
255
|
-
export const
|
|
256
|
-
args: {
|
|
257
|
-
appearance: 'classic',
|
|
258
|
-
},
|
|
255
|
+
export const DefaultLook: Story = {
|
|
259
256
|
parameters: {
|
|
260
257
|
docs: {
|
|
261
258
|
description: {
|
|
262
259
|
story:
|
|
263
|
-
'
|
|
260
|
+
'The default appearance over a filled workflow form, for comparison with **Calm**: tinted Set box, a green Ready and meter, a green dot on every filled row, upper-case group labels and a red/green Yes/No chip.',
|
|
264
261
|
},
|
|
265
262
|
},
|
|
266
263
|
},
|
|
267
264
|
play: async () => {
|
|
268
265
|
await _testsWaitForText('Supplier Payment Release');
|
|
269
|
-
// The
|
|
266
|
+
// The default look is untouched: the tinted box and its count tag.
|
|
270
267
|
await waitFor(() => expect(box('set')).toBeTruthy());
|
|
271
268
|
expect(box('set')!.querySelector('.reqore-panel-title .reqore-tag')).toBeTruthy();
|
|
272
269
|
expect(row('display_name')!.querySelector('.options-readfirst-status-dot')).toBeTruthy();
|
|
@@ -274,11 +271,14 @@ export const ClassicLook: Story = {
|
|
|
274
271
|
};
|
|
275
272
|
|
|
276
273
|
export const Calm: Story = {
|
|
274
|
+
args: {
|
|
275
|
+
appearance: 'calm',
|
|
276
|
+
},
|
|
277
277
|
parameters: {
|
|
278
278
|
docs: {
|
|
279
279
|
description: {
|
|
280
280
|
story:
|
|
281
|
-
'
|
|
281
|
+
'`appearance="calm"` over the same filled form: the sections are plain panels on the theme’s panel surface with label/value rows split by hairlines, labels grey, group headings in sentence case, the count a quiet number, no dot on a healthy row, No as a word, and a short meter whose done part is grey.',
|
|
282
282
|
},
|
|
283
283
|
},
|
|
284
284
|
},
|
|
@@ -350,6 +350,7 @@ export const Calm: Story = {
|
|
|
350
350
|
|
|
351
351
|
export const CalmNeedsAttention: Story = {
|
|
352
352
|
args: {
|
|
353
|
+
appearance: 'calm',
|
|
353
354
|
value: newFormValue,
|
|
354
355
|
// After a submit attempt: every empty required field is now a fault. A form nobody
|
|
355
356
|
// has been to yet reads "To fill in" instead (CalmNewForm).
|
|
@@ -402,6 +403,9 @@ export const CalmNeedsAttention: Story = {
|
|
|
402
403
|
};
|
|
403
404
|
|
|
404
405
|
export const CalmChoiceHeading: Story = {
|
|
406
|
+
args: {
|
|
407
|
+
appearance: 'calm',
|
|
408
|
+
},
|
|
405
409
|
parameters: {
|
|
406
410
|
docs: {
|
|
407
411
|
description: {
|
|
@@ -422,6 +426,7 @@ export const CalmChoiceHeading: Story = {
|
|
|
422
426
|
|
|
423
427
|
export const CalmReadOnly: Story = {
|
|
424
428
|
args: {
|
|
429
|
+
appearance: 'calm',
|
|
425
430
|
readOnly: true,
|
|
426
431
|
value: { ...filledValue, version: { type: 'string', value: '' } },
|
|
427
432
|
},
|
|
@@ -447,6 +452,7 @@ export const CalmReadOnly: Story = {
|
|
|
447
452
|
|
|
448
453
|
export const CalmNewForm: Story = {
|
|
449
454
|
args: {
|
|
455
|
+
appearance: 'calm',
|
|
450
456
|
value: newFormValue,
|
|
451
457
|
},
|
|
452
458
|
parameters: {
|
|
@@ -499,6 +505,7 @@ export const CalmNewForm: Story = {
|
|
|
499
505
|
|
|
500
506
|
export const CalmOnAPhone: Story = {
|
|
501
507
|
args: {
|
|
508
|
+
appearance: 'calm',
|
|
502
509
|
value: newFormValue,
|
|
503
510
|
},
|
|
504
511
|
parameters: {
|
|
@@ -10,17 +10,16 @@ import { createContext, useContext } from 'react';
|
|
|
10
10
|
* How a form looks. The look only: which fields exist, how they bucket, what a
|
|
11
11
|
* click does and what is validated are identical in every appearance.
|
|
12
12
|
*
|
|
13
|
-
* - `'
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* every row.
|
|
13
|
+
* - `'default'` — the established read-first look: tinted status boxes, an
|
|
14
|
+
* intent-coloured completion meter, a status dot on every row.
|
|
15
|
+
* - `'calm'` — colour only where something needs a person. The status boxes are
|
|
16
|
+
* plain panels on the theme's panel surface with label/value rows split by
|
|
17
|
+
* hairlines; labels are grey; a missing required value is an amber word
|
|
18
|
+
* instead of a red asterisk and message; counts are quiet numbers; a healthy
|
|
19
|
+
* row carries no dot; Yes/No are plain words; nothing is forced into
|
|
20
|
+
* uppercase. Opt-in, so an app that has not asked for it does not change.
|
|
22
21
|
*/
|
|
23
|
-
export type TFormEngineAppearance = '
|
|
22
|
+
export type TFormEngineAppearance = 'default' | 'calm';
|
|
24
23
|
|
|
25
24
|
/**
|
|
26
25
|
* The appearance in force for everything below it: the engine's own rows and
|
|
@@ -35,9 +34,9 @@ export const FormAppearanceContext = createContext<TFormEngineAppearance | undef
|
|
|
35
34
|
|
|
36
35
|
export const FormAppearanceProvider = FormAppearanceContext.Provider;
|
|
37
36
|
|
|
38
|
-
/** The appearance in force here; `'
|
|
37
|
+
/** The appearance in force here; `'default'` when nothing set one. */
|
|
39
38
|
export const useFormAppearance = (): TFormEngineAppearance =>
|
|
40
|
-
useContext(FormAppearanceContext) ?? '
|
|
39
|
+
useContext(FormAppearanceContext) ?? 'default';
|
|
41
40
|
|
|
42
41
|
/** Shorthand for the one question most call sites ask. */
|
|
43
42
|
export const useIsCalmForm = (): boolean => useFormAppearance() === 'calm';
|