@isi-ui7/bos7-shared 0.5.1 → 0.5.3

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.
@@ -105,6 +105,23 @@ export type DetailRowsConfig = {
105
105
  * Forwarded to `<EditableTable lockExistingRows>`.
106
106
  */
107
107
  lockExistingRows?: boolean;
108
+ /**
109
+ * Menyembunyikan tombol "Tambah Baris" / aksi "Hapus" per baris.
110
+ *
111
+ * 🔴 Ada karena `maxRows` dan `lockExistingRows` KEDUANYA salah untuk tabel
112
+ * yang barisnya ditentukan master tapi NILAINYA diisi petugas (limit
113
+ * transaksi CDD): `maxRows` men-DISABLE tombolnya (ia tetap ada, dan
114
+ * kontrol yang ada-tapi-nol-pernah-bekerja mengajari petugas bahwa fiturnya
115
+ * rusak), `lockExistingRows` membuat baris termuat READ-ONLY seluruhnya.
116
+ *
117
+ * ⚠️ Keduanya diteruskan APA ADANYA ke `<EditableTable>` (0.2.4+). Prop yang
118
+ * dideklarasikan di sini tapi NOL diteruskan akan lolos tsc, lolos uji tipe,
119
+ * dan mencapai NOL — itu persis keadaan yang berkas ini perbaiki, dan
120
+ * `detail-rows-hide-buttons.test.tsx` menguji SAMPAI (tombol hilang di DOM),
121
+ * bukan ADA (prop hadir di objek config).
122
+ */
123
+ hideAddButton?: boolean;
124
+ hideDeleteAction?: boolean;
108
125
  };
109
126
  /**
110
127
  * Satu kolom pada tabel RINGKAS `detail-modal`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@isi-ui7/bos7-shared",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "Shared auth7 and layout primitives for bos7 applications.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -70,9 +70,9 @@
70
70
  "peerDependencies": {
71
71
  "@carbon/icons-react": "^11.71.0",
72
72
  "@carbon/react": "^1.97.0",
73
- "@isi-ui7/corporate-themes": ">=0.2.19",
73
+ "@isi-ui7/corporate-themes": ">=0.2.21",
74
74
  "@isi-ui7/data-table": ">=0.2.5",
75
- "@isi-ui7/editable-table": ">=0.2.3",
75
+ "@isi-ui7/editable-table": ">=0.2.4",
76
76
  "@isi-ui7/i18n": ">=0.2.4",
77
77
  "@isi-ui7/lookup-input": ">=0.2.4",
78
78
  "@isi-ui7/modal-manager": ">=0.2.0",
@@ -106,14 +106,14 @@
106
106
  "vite": "^5.0.0",
107
107
  "vite-plugin-dts": "^3.6.0",
108
108
  "vitest": "^1.0.0",
109
- "@isi-ui7/corporate-themes": "0.2.19",
110
- "@isi-ui7/i18n": "0.2.4",
111
109
  "@isi-ui7/data-table": "0.2.5",
110
+ "@isi-ui7/editable-table": "0.2.4",
111
+ "@isi-ui7/i18n": "0.2.4",
112
112
  "@isi-ui7/lookup-input": "0.2.4",
113
- "@isi-ui7/modal-manager": "0.2.2",
114
- "@isi-ui7/editable-table": "0.2.3",
115
113
  "@isi-ui7/realtime": "0.2.3",
116
- "@isi-ui7/ui-shell": "0.2.8"
114
+ "@isi-ui7/ui-shell": "0.2.8",
115
+ "@isi-ui7/modal-manager": "0.2.2",
116
+ "@isi-ui7/corporate-themes": "0.2.21"
117
117
  },
118
118
  "dependencies": {
119
119
  "@carbon/icons-react": "^11.71.0",
@@ -127,8 +127,8 @@
127
127
  "jspdf": "^4.2.1",
128
128
  "jspdf-autotable": "^5.0.7",
129
129
  "@isi-ui7/data-table": "0.2.5",
130
- "@isi-ui7/lookup-input": "0.2.4",
131
- "@isi-ui7/editable-table": "0.2.3"
130
+ "@isi-ui7/editable-table": "0.2.4",
131
+ "@isi-ui7/lookup-input": "0.2.4"
132
132
  },
133
133
  "scripts": {
134
134
  "build": "vite build",
@@ -0,0 +1,109 @@
1
+ import { afterEach, describe, expect, it } from "vitest";
2
+ import { cleanup, render, screen } from "@testing-library/react";
3
+ import { SchemaFormRenderer } from "./form-renderer";
4
+ import type { FormSection } from "./form-types";
5
+
6
+ afterEach(cleanup);
7
+
8
+ type D = Record<string, unknown>;
9
+
10
+ // `hideAddButton` / `hideDeleteAction` menembus `detail-rows` sampai ke
11
+ // `<EditableTable>`.
12
+ //
13
+ // 🔴 Kenapa berkas ini menguji DOM dan bukan objek config.
14
+ //
15
+ // Kedua prop mendarat di `@isi-ui7/editable-table` 0.2.4 dengan ujinya sendiri
16
+ // (28 hijau, sabotase dua arah). Lalu NOL SEORANG PUN dapat memakainya: layar
17
+ // nol merender `<EditableTable>` langsung — ia lewat `detail-rows` di paket
18
+ // ini, dan paket ini nol mengenal kedua prop itu. Menyetelnya di skema
19
+ // konsumen mencapai NOL, dan ia terlihat seperti berhasil: tsc hijau, uji
20
+ // hijau, tombolnya tetap ada.
21
+ //
22
+ // ⚠️ Prop yang dideklarasikan di `DetailRowsConfig` tapi nol diteruskan di
23
+ // `form-renderer.tsx` menghasilkan keadaan yang SAMA, satu lapis lebih tinggi
24
+ // — dan uji yang mengasersi "propnya ada di config" akan HIJAU sepanjang
25
+ // waktu. Karena itu asersinya: tombolnya HILANG DI DOM.
26
+ //
27
+ // ⚠️ Dan lengan SEBALIKNYA wajib: prop yang SELALU menyembunyikan lolos uji
28
+ // yang cuma memeriksa `true`.
29
+
30
+ const BARIS = [{ jenis: "Tarik Tunai", nominal: "1000000" }];
31
+
32
+ function sectionDgn(detail: Record<string, unknown>): FormSection<D> {
33
+ return {
34
+ title: "Limit",
35
+ fields: [
36
+ {
37
+ key: "limits",
38
+ label: "Limit Transaksi",
39
+ type: "detail-rows",
40
+ span: 12,
41
+ detailRows: {
42
+ columns: [
43
+ { field: "jenis", header: "Jenis", columnType: { type: "text" } },
44
+ { field: "nominal", header: "Nominal", columnType: { type: "text" } },
45
+ ],
46
+ newRowFactory: () => ({ jenis: "", nominal: "" }),
47
+ ...detail,
48
+ },
49
+ },
50
+ ],
51
+ };
52
+ }
53
+
54
+ function renderDgn(detail: Record<string, unknown> = {}) {
55
+ return render(
56
+ <SchemaFormRenderer<D>
57
+ mode="edit"
58
+ value={{ limits: BARIS }}
59
+ sections={[sectionDgn(detail)]}
60
+ onChange={() => {}}
61
+ />,
62
+ );
63
+ }
64
+
65
+ describe("detail-rows meneruskan hideAddButton / hideDeleteAction ke EditableTable", () => {
66
+ it("BAWAAN (nol diberikan): KEDUA kontrol tampil", () => {
67
+ // 🔴 Kontrol positif untuk kedua uji di bawah. Tanpa lengan ini, sebuah
68
+ // perender yang menyembunyikan SEGALANYA lulus keduanya — dan begitu juga
69
+ // perender yang kebetulan nol merender tabelnya sama sekali.
70
+ renderDgn();
71
+ expect(screen.getByText("Tambah Baris")).toBeTruthy();
72
+ expect(screen.getAllByLabelText("Hapus").length).toBe(1);
73
+ // Dan barisnya memang ADA: kalau tabelnya kosong, "1 tombol Hapus" nol
74
+ // dapat terjadi, jadi asersi ini juga menjaga penyebutnya.
75
+ expect(screen.getByText("Tarik Tunai")).toBeTruthy();
76
+ });
77
+
78
+ it("hideAddButton: Tambah HILANG di DOM, Hapus TETAP", () => {
79
+ renderDgn({ hideAddButton: true });
80
+ expect(screen.queryByText("Tambah Baris")).toBeNull();
81
+ // Lengan yang membuktikan keduanya TERPISAH saat menyeberangi lapis ini —
82
+ // satu flag yang menyembunyikan dua hal lolos tanpa ini.
83
+ expect(screen.getAllByLabelText("Hapus").length).toBe(1);
84
+ });
85
+
86
+ it("hideDeleteAction: Hapus HILANG di DOM, Tambah TETAP", () => {
87
+ renderDgn({ hideDeleteAction: true });
88
+ expect(screen.queryAllByLabelText("Hapus").length).toBe(0);
89
+ expect(screen.getByText("Tambah Baris")).toBeTruthy();
90
+ });
91
+
92
+ it("false eksplisit ⇒ KEDUANYA tetap tampil", () => {
93
+ // Membedakan "diteruskan apa adanya" dari "diteruskan sebagai truthy".
94
+ // `hideAddButton={detail.hideAddButton ?? true}` lolos ketiga uji di atas.
95
+ renderDgn({ hideAddButton: false, hideDeleteAction: false });
96
+ expect(screen.getByText("Tambah Baris")).toBeTruthy();
97
+ expect(screen.getAllByLabelText("Hapus").length).toBe(1);
98
+ });
99
+
100
+ it("keduanya menyala: nol ada kontrol baris, TAPI selnya tetap tersunting", () => {
101
+ // Pembedaan yang paling mudah hilang: `readOnly` juga menyembunyikan
102
+ // kedua tombol — tapi ia MEMBEKUKAN selnya, dan itu cacat yang persis
103
+ // baru diperbaiki di layar limit transaksi.
104
+ renderDgn({ hideAddButton: true, hideDeleteAction: true });
105
+ expect(screen.queryByText("Tambah Baris")).toBeNull();
106
+ expect(screen.queryAllByLabelText("Hapus").length).toBe(0);
107
+ expect(screen.getByText("Tarik Tunai")).toBeTruthy();
108
+ });
109
+ });
@@ -661,6 +661,8 @@ export function SchemaFormRenderer<TData extends Record<string, unknown>>({
661
661
  maxRows={detail.maxRows}
662
662
  readOnly={effectiveReadonly}
663
663
  lockExistingRows={detail.lockExistingRows}
664
+ hideAddButton={detail.hideAddButton}
665
+ hideDeleteAction={detail.hideDeleteAction}
664
666
  validateRow={detail.validateRow}
665
667
  lookupCellRenderer={(args) => {
666
668
  const ct = args.column.columnType;
package/src/form-types.ts CHANGED
@@ -112,6 +112,24 @@ export type DetailRowsConfig = {
112
112
  * Forwarded to `<EditableTable lockExistingRows>`.
113
113
  */
114
114
  lockExistingRows?: boolean;
115
+
116
+ /**
117
+ * Menyembunyikan tombol "Tambah Baris" / aksi "Hapus" per baris.
118
+ *
119
+ * 🔴 Ada karena `maxRows` dan `lockExistingRows` KEDUANYA salah untuk tabel
120
+ * yang barisnya ditentukan master tapi NILAINYA diisi petugas (limit
121
+ * transaksi CDD): `maxRows` men-DISABLE tombolnya (ia tetap ada, dan
122
+ * kontrol yang ada-tapi-nol-pernah-bekerja mengajari petugas bahwa fiturnya
123
+ * rusak), `lockExistingRows` membuat baris termuat READ-ONLY seluruhnya.
124
+ *
125
+ * ⚠️ Keduanya diteruskan APA ADANYA ke `<EditableTable>` (0.2.4+). Prop yang
126
+ * dideklarasikan di sini tapi NOL diteruskan akan lolos tsc, lolos uji tipe,
127
+ * dan mencapai NOL — itu persis keadaan yang berkas ini perbaiki, dan
128
+ * `detail-rows-hide-buttons.test.tsx` menguji SAMPAI (tombol hilang di DOM),
129
+ * bukan ADA (prop hadir di objek config).
130
+ */
131
+ hideAddButton?: boolean;
132
+ hideDeleteAction?: boolean;
115
133
  };
116
134
 
117
135
  /**