@isi-ui7/bos7-shared 0.4.7 → 0.5.0
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/i18n.d.ts +2 -0
- package/dist/{index-C-h8WiEp.js → index-CvKOQKt5.js} +3 -1
- package/dist/{index.es-C4xrluJC.js → index.es-BAzO0Iw8.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/{jspdf.es.min-OHmQQaGr.js → jspdf.es.min-B3CTSFr_.js} +1 -1
- package/package.json +9 -9
- package/src/crud-components.tsx +67 -28
- package/src/crud-filter-fields.test.tsx +77 -8
- package/src/i18n.tsx +4 -0
package/dist/i18n.d.ts
CHANGED
|
@@ -27391,7 +27391,7 @@ function ba(M, L) {
|
|
|
27391
27391
|
return String(pr(M, L));
|
|
27392
27392
|
}
|
|
27393
27393
|
async function Io(M, L, r) {
|
|
27394
|
-
const { default: x } = await import("./jspdf.es.min-
|
|
27394
|
+
const { default: x } = await import("./jspdf.es.min-B3CTSFr_.js").then((o) => o.j), { default: w } = await import("./jspdf.plugin.autotable-DcntYaes.js"), c = new x({ orientation: "landscape" });
|
|
27395
27395
|
c.setFontSize(14), c.text(r, 14, 15), c.setFontSize(10), w(c, {
|
|
27396
27396
|
head: [L.map((o) => o.title)],
|
|
27397
27397
|
body: M.map((o) => L.map((a) => ba(o[a.field], a.displayFormat))),
|
|
@@ -28663,6 +28663,8 @@ function tl() {
|
|
|
28663
28663
|
toggleOff: M("bos7.toggleOff", "Tidak"),
|
|
28664
28664
|
emptyValue: M("bos7.emptyValue", "—"),
|
|
28665
28665
|
showInactive: M("bos7.showInactive", "Tampilkan Nonaktif"),
|
|
28666
|
+
filterApply: M("bos7.filterApply", "Cari"),
|
|
28667
|
+
filterReset: M("bos7.filterReset", "Reset"),
|
|
28666
28668
|
loading: M("bos7.loading", "Memuat data..."),
|
|
28667
28669
|
saving: M("bos7.saving", "Memproses..."),
|
|
28668
28670
|
errorTitle: M("bos7.errorTitle", "Gagal"),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as Ke, g as Do } from "./index-
|
|
2
|
-
import { _ as Xa } from "./jspdf.es.min-
|
|
1
|
+
import { c as Ke, g as Do } from "./index-CvKOQKt5.js";
|
|
2
|
+
import { _ as Xa } from "./jspdf.es.min-B3CTSFr_.js";
|
|
3
3
|
var vt = function(a) {
|
|
4
4
|
return a && a.Math === Math && a;
|
|
5
5
|
}, _ = (
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { m as s, a9 as o, A as t, Y as r, R as i, O as n, l, F as c, G as d, E as u, P as S, a3 as h, a2 as A, a1 as T, a0 as k, af as C, K as _, H as O, I as U, J as g, n as N, ah as P, ag as m, Q as E, i as F, D as p, f, y as I, S as R, j as x, V as v, a8 as y, o as L, h as b, L as B, M as D, T as M, W as H, C as V, e as W, z as q, a7 as K, a5 as w, a6 as z, v as G, aa as Q, ab as X, ac as Y, ad as j, k as J, U as Z, w as $, a4 as aa, Z as ea, _ as sa, p as oa, a as ta, N as ra, q as ia, r as na, $ as la, t as ca, ae as da, s as ua, B as Sa, x as ha, b as Aa, u as Ta, ai as ka, X as Ca, d as _a } from "./index-
|
|
1
|
+
import { m as s, a9 as o, A as t, Y as r, R as i, O as n, l, F as c, G as d, E as u, P as S, a3 as h, a2 as A, a1 as T, a0 as k, af as C, K as _, H as O, I as U, J as g, n as N, ah as P, ag as m, Q as E, i as F, D as p, f, y as I, S as R, j as x, V as v, a8 as y, o as L, h as b, L as B, M as D, T as M, W as H, C as V, e as W, z as q, a7 as K, a5 as w, a6 as z, v as G, aa as Q, ab as X, ac as Y, ad as j, k as J, U as Z, w as $, a4 as aa, Z as ea, _ as sa, p as oa, a as ta, N as ra, q as ia, r as na, $ as la, t as ca, ae as da, s as ua, B as Sa, x as ha, b as Aa, u as Ta, ai as ka, X as Ca, d as _a } from "./index-CvKOQKt5.js";
|
|
2
2
|
export {
|
|
3
3
|
s as AUTH7_SCOPES,
|
|
4
4
|
o as AppShellLayout,
|
|
@@ -9216,7 +9216,7 @@ function(n) {
|
|
|
9216
9216
|
var d = h.getContext("2d");
|
|
9217
9217
|
d.fillStyle = "#fff", d.fillRect(0, 0, h.width, h.height);
|
|
9218
9218
|
var m = { ignoreMouse: !0, ignoreAnimation: !0, ignoreDimensions: !0 }, _ = this;
|
|
9219
|
-
return (Kt.canvg ? Promise.resolve(Kt.canvg) : import("./index.es-
|
|
9219
|
+
return (Kt.canvg ? Promise.resolve(Kt.canvg) : import("./index.es-BAzO0Iw8.js")).catch(function(k) {
|
|
9220
9220
|
return Promise.reject(new Error("Could not load canvg: " + k));
|
|
9221
9221
|
}).then(function(k) {
|
|
9222
9222
|
return k.default ? k.default : k;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@isi-ui7/bos7-shared",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Shared auth7 and layout primitives for bos7 applications.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -70,7 +70,7 @@
|
|
|
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.
|
|
73
|
+
"@isi-ui7/corporate-themes": ">=0.2.18",
|
|
74
74
|
"@isi-ui7/data-table": ">=0.2.5",
|
|
75
75
|
"@isi-ui7/editable-table": ">=0.2.3",
|
|
76
76
|
"@isi-ui7/i18n": ">=0.2.4",
|
|
@@ -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.
|
|
109
|
+
"@isi-ui7/corporate-themes": "0.2.18",
|
|
110
110
|
"@isi-ui7/data-table": "0.2.5",
|
|
111
|
+
"@isi-ui7/editable-table": "0.2.3",
|
|
112
|
+
"@isi-ui7/i18n": "0.2.4",
|
|
111
113
|
"@isi-ui7/lookup-input": "0.2.4",
|
|
112
114
|
"@isi-ui7/realtime": "0.2.3",
|
|
113
|
-
"@isi-ui7/
|
|
114
|
-
"@isi-ui7/
|
|
115
|
-
"@isi-ui7/editable-table": "0.2.3",
|
|
116
|
-
"@isi-ui7/ui-shell": "0.2.8"
|
|
115
|
+
"@isi-ui7/ui-shell": "0.2.8",
|
|
116
|
+
"@isi-ui7/modal-manager": "0.2.2"
|
|
117
117
|
},
|
|
118
118
|
"dependencies": {
|
|
119
119
|
"@carbon/icons-react": "^11.71.0",
|
|
@@ -126,9 +126,9 @@
|
|
|
126
126
|
"jose": "^5.9.6",
|
|
127
127
|
"jspdf": "^4.2.1",
|
|
128
128
|
"jspdf-autotable": "^5.0.7",
|
|
129
|
-
"@isi-ui7/
|
|
129
|
+
"@isi-ui7/data-table": "0.2.5",
|
|
130
130
|
"@isi-ui7/editable-table": "0.2.3",
|
|
131
|
-
"@isi-ui7/
|
|
131
|
+
"@isi-ui7/lookup-input": "0.2.4"
|
|
132
132
|
},
|
|
133
133
|
"scripts": {
|
|
134
134
|
"build": "vite build",
|
package/src/crud-components.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { type ReactNode, useCallback, useEffect,
|
|
4
|
-
import { Add } from "@carbon/icons-react";
|
|
3
|
+
import { type ReactNode, useCallback, useEffect, useState } from "react";
|
|
4
|
+
import { Add, Search } from "@carbon/icons-react";
|
|
5
5
|
import {
|
|
6
6
|
Button,
|
|
7
7
|
ComposedModal,
|
|
@@ -88,36 +88,51 @@ export function SharedCrudListPage({
|
|
|
88
88
|
// Two states per field, not one: `filterFieldValues` is what the widget
|
|
89
89
|
// shows (updates on every keystroke, so typing feels immediate);
|
|
90
90
|
// `filterFieldCommitted` is what actually reaches additionalParams (and
|
|
91
|
-
// therefore triggers a refetch).
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
|
|
91
|
+
// therefore triggers a refetch).
|
|
92
|
+
//
|
|
93
|
+
// ── KEPUTUSAN LAMA, DIGANTI 2026-09-06 — dibiarkan tertulis, bukan dihapus.
|
|
94
|
+
//
|
|
95
|
+
// Semula: `select` commit SEGERA, `text` commit DEBOUNCED 400 ms, mengikuti
|
|
96
|
+
// preseden kotak cari OffsetDataTable. Itu keputusan yang SENGAJA, bukan
|
|
97
|
+
// kelalaian — dan komentarnya dipertahankan supaya orang berikutnya nol
|
|
98
|
+
// mengira debounce nol pernah dipikirkan lalu memasangnya kembali.
|
|
99
|
+
//
|
|
100
|
+
// ── YANG BERLAKU SEKARANG: terapkan MANUAL lewat tombol `Cari`.
|
|
101
|
+
//
|
|
102
|
+
// Diputuskan user 2026-09-06 sesudah melihat layarnya sendiri: "ada tombol
|
|
103
|
+
// search dan reset filter". Itu pernyataan bagaimana layar ini dioperasikan,
|
|
104
|
+
// bukan preferensi kosmetik.
|
|
105
|
+
//
|
|
106
|
+
// Dan alasan teknisnya BERBALIK ARAH sejak catatan lama ditulis: panel ini
|
|
107
|
+
// kini punya SEPULUH medan (Putaran 2, 2026-09-03). Terapkan-otomatis berarti
|
|
108
|
+
// tiap ketukan memicu kueri lintas sepuluh medan. Debounce dipilih saat
|
|
109
|
+
// panelnya kecil; panelnya sudah nol kecil.
|
|
110
|
+
//
|
|
111
|
+
// ⚠️ Kotak `Search` toolbar DataTable NOL disentuh — ia sumbu BERBEDA
|
|
112
|
+
// (full_name ATAU cif_number lewat AltSearchColumns), dan auto-apply
|
|
113
|
+
// ber-debounce di sana tetap benar.
|
|
100
114
|
const [filterFieldValues, setFilterFieldValues] = useState<Record<string, string>>({});
|
|
101
115
|
const [filterFieldCommitted, setFilterFieldCommitted] = useState<Record<string, string>>({});
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const timers = filterFieldTimers.current;
|
|
105
|
-
return () => {
|
|
106
|
-
for (const t of Object.values(timers)) clearTimeout(t);
|
|
107
|
-
};
|
|
108
|
-
}, []);
|
|
116
|
+
// Mengetik/memilih hanya menggerakkan yang TAMPIL. Nol ada yang mencapai
|
|
117
|
+
// additionalParams sampai `Cari` diklik — itu seluruh isi keputusan di atas.
|
|
109
118
|
const setFilterFieldValue = useCallback((field: CrudFilterFieldDef, value: string) => {
|
|
110
119
|
setFilterFieldValues((prev) => ({ ...prev, [field.param]: value }));
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
}, []);
|
|
121
|
+
// Disalin, bukan dirujuk: `filterFieldCommitted` harus jadi POTRET nilai saat
|
|
122
|
+
// Cari diklik. Membaginya dgn `filterFieldValues` akan membuat ketukan
|
|
123
|
+
// berikutnya ikut terkirim tanpa ada yang mengklik apa pun.
|
|
124
|
+
const terapkanFilter = useCallback(() => {
|
|
125
|
+
setFilterFieldValues((v) => {
|
|
126
|
+
setFilterFieldCommitted({ ...v });
|
|
127
|
+
return v;
|
|
128
|
+
});
|
|
129
|
+
}, []);
|
|
130
|
+
// Mengosongkan KEDUANYA. Mengosongkan yang tampil saja akan menyisakan filter
|
|
131
|
+
// yang masih berlaku di balik layar sementara kotaknya terlihat kosong —
|
|
132
|
+
// pengguna melihat "nol ada filter" dan hasilnya tetap tersaring.
|
|
133
|
+
const resetFilter = useCallback(() => {
|
|
134
|
+
setFilterFieldValues({});
|
|
135
|
+
setFilterFieldCommitted({});
|
|
121
136
|
}, []);
|
|
122
137
|
const hasFilterFields = Boolean(schema.filterFields?.length);
|
|
123
138
|
|
|
@@ -207,10 +222,34 @@ export function SharedCrudListPage({
|
|
|
207
222
|
placeholder={f.placeholder ?? f.label}
|
|
208
223
|
value={filterFieldValues[f.param] ?? ""}
|
|
209
224
|
onChange={(e) => setFilterFieldValue(f, e.target.value)}
|
|
225
|
+
onKeyDown={(e) => {
|
|
226
|
+
if (e.key === "Enter") terapkanFilter();
|
|
227
|
+
}}
|
|
210
228
|
/>
|
|
211
229
|
</div>
|
|
212
230
|
),
|
|
213
231
|
)}
|
|
232
|
+
{hasFilterFields ? (
|
|
233
|
+
<>
|
|
234
|
+
<Button
|
|
235
|
+
kind="primary"
|
|
236
|
+
size="sm"
|
|
237
|
+
renderIcon={Search}
|
|
238
|
+
onClick={terapkanFilter}
|
|
239
|
+
data-testid="filter-apply"
|
|
240
|
+
>
|
|
241
|
+
{labels.filterApply}
|
|
242
|
+
</Button>
|
|
243
|
+
<Button
|
|
244
|
+
kind="ghost"
|
|
245
|
+
size="sm"
|
|
246
|
+
onClick={resetFilter}
|
|
247
|
+
data-testid="filter-reset"
|
|
248
|
+
>
|
|
249
|
+
{labels.filterReset}
|
|
250
|
+
</Button>
|
|
251
|
+
</>
|
|
252
|
+
) : null}
|
|
214
253
|
{toolbarExtra}
|
|
215
254
|
{(schema.showAddButton ?? true) ? (
|
|
216
255
|
<Button renderIcon={Add} size="sm" onClick={onAdd}>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import { render, screen
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
3
|
import userEvent from "@testing-library/user-event";
|
|
4
4
|
|
|
5
5
|
// K-6 (user, 2026-08-26): CrudListSchema.filterFields — N granular toolbar
|
|
@@ -46,7 +46,7 @@ describe("CrudListSchema.filterFields", () => {
|
|
|
46
46
|
expect(paramsRendered()).toEqual({});
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
it("field type:select
|
|
49
|
+
it("field type:select MENUNGGU tombol Cari (dulu: kirim SEGERA saat dipilih)", async () => {
|
|
50
50
|
const user = userEvent.setup();
|
|
51
51
|
render(
|
|
52
52
|
<SharedCrudListPage
|
|
@@ -70,10 +70,16 @@ describe("CrudListSchema.filterFields", () => {
|
|
|
70
70
|
const select = screen.getByLabelText("Jenis Identitas") as HTMLSelectElement;
|
|
71
71
|
await user.selectOptions(select, "KTP");
|
|
72
72
|
|
|
73
|
+
// 🔑 Perilaku DIGANTI 2026-09-06 (keputusan user): memilih nol lagi
|
|
74
|
+
// mengirim apa pun. Sebelumnya baris ini berbunyi { identity_type: "KTP" }.
|
|
75
|
+
expect(paramsRendered()).toEqual({ identity_type: "" });
|
|
76
|
+
expect(select.value).toBe("KTP"); // widget tetap responsif
|
|
77
|
+
|
|
78
|
+
await user.click(screen.getByTestId("filter-apply"));
|
|
73
79
|
expect(paramsRendered()).toEqual({ identity_type: "KTP" });
|
|
74
80
|
});
|
|
75
81
|
|
|
76
|
-
it("field type:text
|
|
82
|
+
it("field type:text MENUNGGU tombol Cari (dulu: DEBOUNCE 400 ms)", async () => {
|
|
77
83
|
const user = userEvent.setup();
|
|
78
84
|
render(
|
|
79
85
|
<SharedCrudListPage
|
|
@@ -90,13 +96,69 @@ describe("CrudListSchema.filterFields", () => {
|
|
|
90
96
|
const input = screen.getByLabelText("Nama") as HTMLInputElement;
|
|
91
97
|
await user.type(input, "Budi");
|
|
92
98
|
|
|
93
|
-
// 🔑 Klaim inti: mengetik
|
|
94
|
-
|
|
95
|
-
//
|
|
99
|
+
// 🔑 Klaim inti: mengetik nol mengirim param.
|
|
100
|
+
expect(paramsRendered()).toEqual({ full_name: "" });
|
|
101
|
+
expect(input.value).toBe("Budi"); // widget tetap responsif
|
|
102
|
+
|
|
103
|
+
// 🔴 KONTROL WAKTU: dulu param mendarat sendiri sesudah debounce 400 ms.
|
|
104
|
+
// Menunggu LEBIH LAMA dari itu dan tetap kosong membuktikan auto-apply
|
|
105
|
+
// benar-benar HILANG — bukan sekadar lebih lambat.
|
|
106
|
+
await new Promise((r) => setTimeout(r, 700));
|
|
96
107
|
expect(paramsRendered()).toEqual({ full_name: "" });
|
|
97
|
-
expect(input.value).toBe("Budi"); // tapi WIDGET-nya tetap responsif
|
|
98
108
|
|
|
99
|
-
await
|
|
109
|
+
await user.click(screen.getByTestId("filter-apply"));
|
|
110
|
+
expect(paramsRendered()).toEqual({ full_name: "Budi" });
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("Enter di medan teks setara mengklik Cari", async () => {
|
|
114
|
+
const user = userEvent.setup();
|
|
115
|
+
render(
|
|
116
|
+
<SharedCrudListPage
|
|
117
|
+
schema={{ ...dasar, filterFields: [{ type: "text", param: "full_name", label: "Nama" }] }}
|
|
118
|
+
onAdd={() => {}}
|
|
119
|
+
onPopupClick={() => {}}
|
|
120
|
+
/>,
|
|
121
|
+
);
|
|
122
|
+
const input = screen.getByLabelText("Nama") as HTMLInputElement;
|
|
123
|
+
await user.type(input, "Budi{Enter}");
|
|
124
|
+
expect(paramsRendered()).toEqual({ full_name: "Budi" });
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("Reset mengosongkan SELURUH medan — dihitung, bukan dilihat", async () => {
|
|
128
|
+
const user = userEvent.setup();
|
|
129
|
+
const banyak = Array.from({ length: 10 }, (_, i) => ({
|
|
130
|
+
type: "text" as const, param: `p${i}`, label: `Medan ${i}`,
|
|
131
|
+
}));
|
|
132
|
+
render(
|
|
133
|
+
<SharedCrudListPage
|
|
134
|
+
schema={{ ...dasar, filterFields: banyak }}
|
|
135
|
+
onAdd={() => {}}
|
|
136
|
+
onPopupClick={() => {}}
|
|
137
|
+
/>,
|
|
138
|
+
);
|
|
139
|
+
for (const f of banyak) {
|
|
140
|
+
await user.type(screen.getByLabelText(f.label) as HTMLInputElement, "x");
|
|
141
|
+
}
|
|
142
|
+
await user.click(screen.getByTestId("filter-apply"));
|
|
143
|
+
const terkirim = paramsRendered() as Record<string, string>;
|
|
144
|
+
// KONTROL POSITIF: kesepuluh benar-benar terisi lebih dulu. Tanpa ini,
|
|
145
|
+
// "Reset mengosongkan" lulus juga pada panel yang memang sudah kosong.
|
|
146
|
+
expect(Object.values(terkirim).filter((v) => v === "x")).toHaveLength(10);
|
|
147
|
+
|
|
148
|
+
await user.click(screen.getByTestId("filter-reset"));
|
|
149
|
+
const sesudah = paramsRendered() as Record<string, string>;
|
|
150
|
+
expect(Object.values(sesudah).filter((v) => v !== "")).toHaveLength(0);
|
|
151
|
+
// dan yang TAMPIL ikut kosong — mengosongkan salah satunya saja menyisakan
|
|
152
|
+
// filter yang masih berlaku di balik kotak yang terlihat bersih.
|
|
153
|
+
for (const f of banyak) {
|
|
154
|
+
expect((screen.getByLabelText(f.label) as HTMLInputElement).value).toBe("");
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("tombol Cari/Reset NOL muncul saat filterFields diabaikan", () => {
|
|
159
|
+
render(<SharedCrudListPage schema={dasar} onAdd={() => {}} onPopupClick={() => {}} />);
|
|
160
|
+
expect(screen.queryByTestId("filter-apply")).toBeNull();
|
|
161
|
+
expect(screen.queryByTestId("filter-reset")).toBeNull();
|
|
100
162
|
});
|
|
101
163
|
|
|
102
164
|
it("filterSelect (lama) DAN filterFields (baru) coexist tanpa saling menimpa", async () => {
|
|
@@ -122,6 +184,10 @@ describe("CrudListSchema.filterFields", () => {
|
|
|
122
184
|
expect(paramsRendered()).toEqual({ status: "", branch_code: "" });
|
|
123
185
|
|
|
124
186
|
await user.selectOptions(screen.getByLabelText("Cabang"), "001");
|
|
187
|
+
await user.click(screen.getByTestId("filter-apply"));
|
|
188
|
+
// filterSelect (lama, `status`) NOL disentuh perubahan 2026-09-06 — ia
|
|
189
|
+
// sumbu terpisah dan tetap punya jalurnya sendiri; yang kini menunggu
|
|
190
|
+
// tombol hanyalah filterFields (`branch_code`).
|
|
125
191
|
expect(paramsRendered()).toEqual({ status: "", branch_code: "001" });
|
|
126
192
|
});
|
|
127
193
|
|
|
@@ -151,6 +217,9 @@ describe("CrudListSchema.filterFields", () => {
|
|
|
151
217
|
/>,
|
|
152
218
|
);
|
|
153
219
|
await user.selectOptions(screen.getByLabelText("Jenis Identitas"), "KTP");
|
|
220
|
+
await user.click(screen.getByTestId("filter-apply"));
|
|
221
|
+
// 🔑 Klaim yang dijaga uji ini NOL berubah: satu medan bergerak, yang lain
|
|
222
|
+
// tetap kosong. Yang berubah cuma KAPAN ia terkirim.
|
|
154
223
|
expect(paramsRendered()).toEqual({ identity_type: "KTP", branch_code: "" });
|
|
155
224
|
});
|
|
156
225
|
});
|
package/src/i18n.tsx
CHANGED
|
@@ -25,6 +25,8 @@ export type BosSharedLabels = {
|
|
|
25
25
|
toggleOff: string;
|
|
26
26
|
emptyValue: string;
|
|
27
27
|
showInactive: string;
|
|
28
|
+
filterApply: string;
|
|
29
|
+
filterReset: string;
|
|
28
30
|
|
|
29
31
|
loading: string;
|
|
30
32
|
saving: string;
|
|
@@ -91,6 +93,8 @@ export function useBosSharedI18n(): BosSharedLabels {
|
|
|
91
93
|
toggleOff: t("bos7.toggleOff", "Tidak"),
|
|
92
94
|
emptyValue: t("bos7.emptyValue", "—"),
|
|
93
95
|
showInactive: t("bos7.showInactive", "Tampilkan Nonaktif"),
|
|
96
|
+
filterApply: t("bos7.filterApply", "Cari"),
|
|
97
|
+
filterReset: t("bos7.filterReset", "Reset"),
|
|
94
98
|
|
|
95
99
|
loading: t("bos7.loading", "Memuat data..."),
|
|
96
100
|
saving: t("bos7.saving", "Memproses..."),
|