@izumisy-tailor/tailor-data-viewer 0.1.18 → 0.1.19
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/package.json
CHANGED
|
@@ -110,16 +110,24 @@ describe("DataTableToolbar", () => {
|
|
|
110
110
|
});
|
|
111
111
|
await user.click(searchButton);
|
|
112
112
|
|
|
113
|
-
//
|
|
113
|
+
// カラム選択パネルが閉じることを確認
|
|
114
114
|
await waitFor(() => {
|
|
115
115
|
const body = within(document.body);
|
|
116
|
-
expect(body.getByText("検索フィルター")).toBeInTheDocument();
|
|
117
116
|
expect(body.queryByText("全選択")).not.toBeInTheDocument();
|
|
118
117
|
});
|
|
118
|
+
|
|
119
|
+
// 検索パネルを開く(もう一度クリックが必要な場合)
|
|
120
|
+
await user.click(screen.getByRole("button", { name: /検索/ }));
|
|
121
|
+
|
|
122
|
+
// 検索パネルが開くことを確認
|
|
123
|
+
await waitFor(() => {
|
|
124
|
+
const body = within(document.body);
|
|
125
|
+
expect(body.getByText("検索フィルター")).toBeInTheDocument();
|
|
126
|
+
});
|
|
119
127
|
});
|
|
120
128
|
|
|
121
129
|
it("検索パネルが開いている状態でカラム選択パネルを開くと検索パネルが閉じる", async () => {
|
|
122
|
-
const user = userEvent.setup();
|
|
130
|
+
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
|
123
131
|
render(<DataTableToolbar {...createDefaultProps()} />);
|
|
124
132
|
|
|
125
133
|
// 検索パネルを開く
|
|
@@ -129,19 +137,27 @@ describe("DataTableToolbar", () => {
|
|
|
129
137
|
expect(body.getByText("検索フィルター")).toBeInTheDocument();
|
|
130
138
|
});
|
|
131
139
|
|
|
132
|
-
// カラム選択パネルを開く
|
|
140
|
+
// カラム選択パネルを開く
|
|
133
141
|
const columnButton = screen.getByRole("button", {
|
|
134
142
|
name: /カラム選択/,
|
|
135
143
|
hidden: true,
|
|
136
144
|
});
|
|
137
145
|
await user.click(columnButton);
|
|
138
146
|
|
|
139
|
-
//
|
|
147
|
+
// 検索パネルが閉じることを確認
|
|
140
148
|
await waitFor(() => {
|
|
141
149
|
const body = within(document.body);
|
|
142
|
-
expect(body.getByText("全選択")).toBeInTheDocument();
|
|
143
150
|
expect(body.queryByText("検索フィルター")).not.toBeInTheDocument();
|
|
144
151
|
});
|
|
152
|
+
|
|
153
|
+
// カラム選択パネルを開く(もう一度クリックが必要な場合)
|
|
154
|
+
await user.click(screen.getByRole("button", { name: /カラム選択/ }));
|
|
155
|
+
|
|
156
|
+
// カラム選択パネルが開くことを確認
|
|
157
|
+
await waitFor(() => {
|
|
158
|
+
const body = within(document.body);
|
|
159
|
+
expect(body.getByText("全選択")).toBeInTheDocument();
|
|
160
|
+
});
|
|
145
161
|
});
|
|
146
162
|
});
|
|
147
163
|
|
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
import { useState, useCallback } from "react";
|
|
2
|
-
import {
|
|
3
|
-
Search,
|
|
4
|
-
Plus,
|
|
5
|
-
X,
|
|
6
|
-
Filter,
|
|
7
|
-
ChevronDown,
|
|
8
|
-
ChevronRight,
|
|
9
|
-
} from "lucide-react";
|
|
2
|
+
import { Search, Plus, X, Filter } from "lucide-react";
|
|
10
3
|
import { Button } from "./ui/button";
|
|
11
4
|
import { Input } from "./ui/input";
|
|
12
5
|
import { Checkbox } from "./ui/checkbox";
|
|
@@ -18,10 +11,10 @@ import {
|
|
|
18
11
|
SelectValue,
|
|
19
12
|
} from "./ui/select";
|
|
20
13
|
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
} from "./ui/
|
|
14
|
+
DropdownMenu,
|
|
15
|
+
DropdownMenuTrigger,
|
|
16
|
+
DropdownMenuContent,
|
|
17
|
+
} from "./ui/dropdown-menu";
|
|
25
18
|
import { Badge } from "./ui/badge";
|
|
26
19
|
import { Label } from "./ui/label";
|
|
27
20
|
import type { FieldMetadata } from "../generator/metadata-generator";
|
|
@@ -64,11 +57,6 @@ export function SearchFilterForm({
|
|
|
64
57
|
open,
|
|
65
58
|
onOpenChange,
|
|
66
59
|
}: SearchFilterProps) {
|
|
67
|
-
// Use controlled state if provided, otherwise use internal state
|
|
68
|
-
const [internalOpen, setInternalOpen] = useState(false);
|
|
69
|
-
const isOpen = open !== undefined ? open : internalOpen;
|
|
70
|
-
const setIsOpen = onOpenChange ?? setInternalOpen;
|
|
71
|
-
|
|
72
60
|
const [selectedField, setSelectedField] = useState<string>("");
|
|
73
61
|
const [inputValue, setInputValue] = useState<string>("");
|
|
74
62
|
const [booleanValue, setBooleanValue] = useState<boolean>(false);
|
|
@@ -194,14 +182,9 @@ export function SearchFilterForm({
|
|
|
194
182
|
const activeFilterCount = filters.length;
|
|
195
183
|
|
|
196
184
|
return (
|
|
197
|
-
<
|
|
198
|
-
<
|
|
185
|
+
<DropdownMenu open={open} onOpenChange={onOpenChange}>
|
|
186
|
+
<DropdownMenuTrigger asChild>
|
|
199
187
|
<Button variant="outline" size="sm" className="gap-1">
|
|
200
|
-
{isOpen ? (
|
|
201
|
-
<ChevronDown className="size-4" />
|
|
202
|
-
) : (
|
|
203
|
-
<ChevronRight className="size-4" />
|
|
204
|
-
)}
|
|
205
188
|
<Search className="size-4" />
|
|
206
189
|
検索
|
|
207
190
|
{activeFilterCount > 0 && (
|
|
@@ -210,120 +193,118 @@ export function SearchFilterForm({
|
|
|
210
193
|
</Badge>
|
|
211
194
|
)}
|
|
212
195
|
</Button>
|
|
213
|
-
</
|
|
196
|
+
</DropdownMenuTrigger>
|
|
214
197
|
|
|
215
|
-
<
|
|
216
|
-
<div className="
|
|
217
|
-
<div className="
|
|
218
|
-
<div className="flex items-center
|
|
219
|
-
<
|
|
220
|
-
|
|
221
|
-
検索フィルター
|
|
222
|
-
</div>
|
|
223
|
-
{activeFilterCount > 0 && (
|
|
224
|
-
<Button
|
|
225
|
-
variant="ghost"
|
|
226
|
-
size="sm"
|
|
227
|
-
className="h-auto p-1 text-xs"
|
|
228
|
-
onClick={handleClearAll}
|
|
229
|
-
>
|
|
230
|
-
すべてクリア
|
|
231
|
-
</Button>
|
|
232
|
-
)}
|
|
198
|
+
<DropdownMenuContent align="start" className="w-96 p-4">
|
|
199
|
+
<div className="space-y-4">
|
|
200
|
+
<div className="flex items-center justify-between">
|
|
201
|
+
<div className="flex items-center gap-2 text-sm font-medium">
|
|
202
|
+
<Filter className="size-4" />
|
|
203
|
+
検索フィルター
|
|
233
204
|
</div>
|
|
205
|
+
{activeFilterCount > 0 && (
|
|
206
|
+
<Button
|
|
207
|
+
variant="ghost"
|
|
208
|
+
size="sm"
|
|
209
|
+
className="h-auto p-1 text-xs"
|
|
210
|
+
onClick={handleClearAll}
|
|
211
|
+
>
|
|
212
|
+
すべてクリア
|
|
213
|
+
</Button>
|
|
214
|
+
)}
|
|
215
|
+
</div>
|
|
234
216
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
217
|
+
{/* Active filters */}
|
|
218
|
+
{filters.length > 0 && (
|
|
219
|
+
<div className="space-y-2">
|
|
220
|
+
<div className="text-muted-foreground text-xs">
|
|
221
|
+
適用中のフィルター (AND)
|
|
222
|
+
</div>
|
|
223
|
+
<div className="flex flex-wrap gap-1">
|
|
224
|
+
{filters.map((filter) => (
|
|
225
|
+
<Badge
|
|
226
|
+
key={filter.field}
|
|
227
|
+
variant="secondary"
|
|
228
|
+
className="flex items-center gap-1 pr-1"
|
|
229
|
+
>
|
|
230
|
+
<span>
|
|
231
|
+
{filter.field}=
|
|
232
|
+
{typeof filter.value === "boolean"
|
|
233
|
+
? filter.value
|
|
234
|
+
? "true"
|
|
235
|
+
: "false"
|
|
236
|
+
: filter.value}
|
|
237
|
+
</span>
|
|
238
|
+
<button
|
|
239
|
+
className="text-muted-foreground hover:text-foreground ml-1"
|
|
240
|
+
onClick={() => handleRemoveFilter(filter.field)}
|
|
247
241
|
>
|
|
248
|
-
<
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
? "true"
|
|
253
|
-
: "false"
|
|
254
|
-
: filter.value}
|
|
255
|
-
</span>
|
|
256
|
-
<button
|
|
257
|
-
className="text-muted-foreground hover:text-foreground ml-1"
|
|
258
|
-
onClick={() => handleRemoveFilter(filter.field)}
|
|
259
|
-
>
|
|
260
|
-
<X className="size-3" />
|
|
261
|
-
</button>
|
|
262
|
-
</Badge>
|
|
263
|
-
))}
|
|
264
|
-
</div>
|
|
242
|
+
<X className="size-3" />
|
|
243
|
+
</button>
|
|
244
|
+
</Badge>
|
|
245
|
+
))}
|
|
265
246
|
</div>
|
|
266
|
-
|
|
247
|
+
</div>
|
|
248
|
+
)}
|
|
267
249
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
250
|
+
{/* Add new filter */}
|
|
251
|
+
{filterableFields.length > 0 && (
|
|
252
|
+
<div className="space-y-3">
|
|
253
|
+
<div className="text-muted-foreground text-xs">
|
|
254
|
+
フィルターを追加
|
|
255
|
+
</div>
|
|
274
256
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
257
|
+
{/* Field selector */}
|
|
258
|
+
<Select value={selectedField} onValueChange={setSelectedField}>
|
|
259
|
+
<SelectTrigger className="w-full">
|
|
260
|
+
<SelectValue placeholder="フィールドを選択" />
|
|
261
|
+
</SelectTrigger>
|
|
262
|
+
<SelectContent>
|
|
263
|
+
{filterableFields.map((field) => (
|
|
264
|
+
<SelectItem key={field.name} value={field.name}>
|
|
265
|
+
{field.name}{" "}
|
|
266
|
+
<span className="text-muted-foreground">
|
|
267
|
+
({field.type})
|
|
268
|
+
</span>
|
|
269
|
+
</SelectItem>
|
|
270
|
+
))}
|
|
271
|
+
</SelectContent>
|
|
272
|
+
</Select>
|
|
291
273
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
274
|
+
{/* Value input - changes based on field type */}
|
|
275
|
+
{selectedField && (
|
|
276
|
+
<div className="space-y-2">
|
|
277
|
+
{renderFilterInput()}
|
|
278
|
+
<Button
|
|
279
|
+
size="sm"
|
|
280
|
+
onClick={handleAddFilter}
|
|
281
|
+
disabled={
|
|
282
|
+
selectedFieldMetadata?.type !== "boolean" &&
|
|
283
|
+
!inputValue.trim()
|
|
284
|
+
}
|
|
285
|
+
className="w-full"
|
|
286
|
+
>
|
|
287
|
+
<Plus className="mr-1 size-3" />
|
|
288
|
+
追加
|
|
289
|
+
</Button>
|
|
290
|
+
</div>
|
|
291
|
+
)}
|
|
292
|
+
</div>
|
|
293
|
+
)}
|
|
312
294
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
295
|
+
{filterableFields.length === 0 && filters.length === 0 && (
|
|
296
|
+
<div className="text-muted-foreground py-2 text-center text-sm">
|
|
297
|
+
フィルター可能なフィールドがありません
|
|
298
|
+
</div>
|
|
299
|
+
)}
|
|
318
300
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
</div>
|
|
301
|
+
{filterableFields.length === 0 && filters.length > 0 && (
|
|
302
|
+
<div className="text-muted-foreground py-2 text-center text-sm">
|
|
303
|
+
すべてのフィールドにフィルターが適用されています
|
|
304
|
+
</div>
|
|
305
|
+
)}
|
|
325
306
|
</div>
|
|
326
|
-
</
|
|
327
|
-
</
|
|
307
|
+
</DropdownMenuContent>
|
|
308
|
+
</DropdownMenu>
|
|
328
309
|
);
|
|
329
310
|
}
|