@isi-ui7/bos7-shared 0.3.4 → 0.3.5
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/form-types.d.ts +13 -0
- package/package.json +1 -1
- package/src/form-renderer.tsx +7 -0
- package/src/form-section-hidden.test.tsx +87 -0
- package/src/form-types.ts +13 -0
package/dist/form-types.d.ts
CHANGED
|
@@ -165,6 +165,19 @@ export type FormSection<TData extends Record<string, unknown>> = {
|
|
|
165
165
|
mode: FormMode;
|
|
166
166
|
disabled: boolean;
|
|
167
167
|
}) => ReactNode;
|
|
168
|
+
/**
|
|
169
|
+
* Sembunyikan SELURUH section — judul, deskripsi, dan isinya.
|
|
170
|
+
*
|
|
171
|
+
* Bentuknya sengaja sama persis dengan `FormField.hidden` supaya tidak ada
|
|
172
|
+
* dua idiom untuk hal yang sama.
|
|
173
|
+
*
|
|
174
|
+
* Ada karena menyembunyikan tiap field satu per satu TIDAK cukup: judul dan
|
|
175
|
+
* deskripsi section tetap terender, meninggalkan kepala panel kosong yang
|
|
176
|
+
* terlihat seperti bug — dan pada form yang panelnya memang harus terkunci
|
|
177
|
+
* dulu, kepala kosong itu justru mengiklankan keberadaan bagian yang belum
|
|
178
|
+
* boleh diisi.
|
|
179
|
+
*/
|
|
180
|
+
hidden?: boolean | ((mode: FormMode, data: TData) => boolean);
|
|
168
181
|
};
|
|
169
182
|
export type FormPageDef<TData extends Record<string, unknown>> = {
|
|
170
183
|
label: string;
|
package/package.json
CHANGED
package/src/form-renderer.tsx
CHANGED
|
@@ -484,6 +484,13 @@ export function SchemaFormRenderer<TData extends Record<string, unknown>>({
|
|
|
484
484
|
style={{ display: "grid", gap: dt.sectionGap }}
|
|
485
485
|
>
|
|
486
486
|
{sections.map((section, sectionIndex) => {
|
|
487
|
+
// Section tersembunyi mengembalikan null — React tidak merender apa
|
|
488
|
+
// pun untuknya, termasuk judul & deskripsinya.
|
|
489
|
+
const sectionHidden =
|
|
490
|
+
typeof section.hidden === "function"
|
|
491
|
+
? section.hidden(mode, value)
|
|
492
|
+
: section.hidden === true;
|
|
493
|
+
if (sectionHidden) return null;
|
|
487
494
|
const cols = section.columns ?? 1;
|
|
488
495
|
const defaultSpan = Math.floor(12 / cols);
|
|
489
496
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import { SchemaFormRenderer } from "./form-renderer";
|
|
4
|
+
import type { FormSection } from "./form-types";
|
|
5
|
+
|
|
6
|
+
type D = Record<string, unknown>;
|
|
7
|
+
|
|
8
|
+
// `FormSection.hidden` — menyembunyikan SELURUH section, bukan hanya field-nya.
|
|
9
|
+
//
|
|
10
|
+
// Menyembunyikan tiap field satu per satu tidak cukup: judul dan deskripsi
|
|
11
|
+
// section tetap terender, meninggalkan kepala panel kosong. Pada form yang
|
|
12
|
+
// panelnya memang harus terkunci dulu, kepala kosong itu justru mengiklankan
|
|
13
|
+
// bagian yang belum boleh diisi.
|
|
14
|
+
|
|
15
|
+
const locked: FormSection<D> = {
|
|
16
|
+
title: "Data Diri",
|
|
17
|
+
description: "Terbuka setelah verifikasi",
|
|
18
|
+
fields: [{ key: "nama", label: "Nama Lengkap", type: "text", span: 4 }],
|
|
19
|
+
hidden: (_mode, data) => data.verified !== true,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const always: FormSection<D> = {
|
|
23
|
+
title: "Verifikasi Identitas",
|
|
24
|
+
fields: [{ key: "nik", label: "Nomor Identitas", type: "text", span: 4 }],
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function renderWith(value: D) {
|
|
28
|
+
return render(
|
|
29
|
+
<SchemaFormRenderer<D>
|
|
30
|
+
mode="create"
|
|
31
|
+
value={value}
|
|
32
|
+
sections={[always, locked]}
|
|
33
|
+
onChange={() => {}}
|
|
34
|
+
/>,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe("FormSection.hidden", () => {
|
|
39
|
+
it("menyembunyikan JUDUL dan DESKRIPSI, bukan cuma field-nya", () => {
|
|
40
|
+
renderWith({ verified: false });
|
|
41
|
+
expect(screen.queryByText("Data Diri")).toBeNull();
|
|
42
|
+
expect(screen.queryByText("Terbuka setelah verifikasi")).toBeNull();
|
|
43
|
+
expect(screen.queryByLabelText("Nama Lengkap")).toBeNull();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("menampilkan section ketika predikatnya false", () => {
|
|
47
|
+
renderWith({ verified: true });
|
|
48
|
+
expect(screen.getByText("Data Diri")).toBeTruthy();
|
|
49
|
+
expect(screen.getByLabelText("Nama Lengkap")).toBeTruthy();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("KONTROL: section tetangga tanpa hidden tidak ikut terpengaruh", () => {
|
|
53
|
+
// Kalau kontrol ini ikut gagal, yang diukur bukan `hidden` melainkan
|
|
54
|
+
// perenderan section secara umum.
|
|
55
|
+
renderWith({ verified: false });
|
|
56
|
+
expect(screen.getByText("Verifikasi Identitas")).toBeTruthy();
|
|
57
|
+
expect(screen.getByLabelText("Nomor Identitas")).toBeTruthy();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("menerima boolean, bukan hanya fungsi — bentuknya sama dgn FormField.hidden", () => {
|
|
61
|
+
render(
|
|
62
|
+
<SchemaFormRenderer<D>
|
|
63
|
+
mode="create"
|
|
64
|
+
value={{}}
|
|
65
|
+
sections={[{ title: "Selalu Tersembunyi", fields: [], hidden: true }]}
|
|
66
|
+
onChange={() => {}}
|
|
67
|
+
/>,
|
|
68
|
+
);
|
|
69
|
+
expect(screen.queryByText("Selalu Tersembunyi")).toBeNull();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("predikat menerima mode — section bisa disembunyikan hanya saat create", () => {
|
|
73
|
+
const s: FormSection<D> = {
|
|
74
|
+
title: "Hanya Saat Edit",
|
|
75
|
+
fields: [],
|
|
76
|
+
hidden: (mode) => mode === "create",
|
|
77
|
+
};
|
|
78
|
+
const { unmount } = render(
|
|
79
|
+
<SchemaFormRenderer<D> mode="create" value={{}} sections={[s]} onChange={() => {}} />,
|
|
80
|
+
);
|
|
81
|
+
expect(screen.queryByText("Hanya Saat Edit")).toBeNull();
|
|
82
|
+
unmount();
|
|
83
|
+
|
|
84
|
+
render(<SchemaFormRenderer<D> mode="edit" value={{}} sections={[s]} onChange={() => {}} />);
|
|
85
|
+
expect(screen.getByText("Hanya Saat Edit")).toBeTruthy();
|
|
86
|
+
});
|
|
87
|
+
});
|
package/src/form-types.ts
CHANGED
|
@@ -177,6 +177,19 @@ export type FormSection<TData extends Record<string, unknown>> = {
|
|
|
177
177
|
mode: FormMode;
|
|
178
178
|
disabled: boolean;
|
|
179
179
|
}) => ReactNode;
|
|
180
|
+
/**
|
|
181
|
+
* Sembunyikan SELURUH section — judul, deskripsi, dan isinya.
|
|
182
|
+
*
|
|
183
|
+
* Bentuknya sengaja sama persis dengan `FormField.hidden` supaya tidak ada
|
|
184
|
+
* dua idiom untuk hal yang sama.
|
|
185
|
+
*
|
|
186
|
+
* Ada karena menyembunyikan tiap field satu per satu TIDAK cukup: judul dan
|
|
187
|
+
* deskripsi section tetap terender, meninggalkan kepala panel kosong yang
|
|
188
|
+
* terlihat seperti bug — dan pada form yang panelnya memang harus terkunci
|
|
189
|
+
* dulu, kepala kosong itu justru mengiklankan keberadaan bagian yang belum
|
|
190
|
+
* boleh diisi.
|
|
191
|
+
*/
|
|
192
|
+
hidden?: boolean | ((mode: FormMode, data: TData) => boolean);
|
|
180
193
|
};
|
|
181
194
|
|
|
182
195
|
// ── Layout ────────────────────────────────────────────────────────────────────
|