@salesforce/webapp-template-app-react-template-b2e-experimental 1.112.6 → 1.112.8
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/CHANGELOG.md +16 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/package.json +3 -3
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/api/graphqlClient.ts +25 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/features/object-search/__examples__/pages/AccountSearch.tsx +82 -54
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/features/object-search/components/FilterContext.tsx +73 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/features/object-search/components/PaginationControls.tsx +45 -87
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/features/object-search/components/filters/BooleanFilter.tsx +16 -36
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/features/object-search/components/filters/DateFilter.tsx +33 -77
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/features/object-search/components/filters/DateRangeFilter.tsx +14 -23
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/features/object-search/components/filters/MultiSelectFilter.tsx +18 -26
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/features/object-search/components/filters/NumericRangeFilter.tsx +22 -39
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/features/object-search/components/filters/SearchFilter.tsx +12 -15
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/features/object-search/components/filters/SelectFilter.tsx +30 -34
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/features/object-search/components/filters/TextFilter.tsx +27 -30
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/features/object-search/hooks/useAsyncData.ts +1 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/features/object-search/hooks/useCachedAsyncData.ts +1 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/features/object-search/hooks/useObjectSearchParams.ts +22 -0
- package/dist/package-lock.json +2 -2
- package/dist/package.json +1 -1
- package/package.json +1 -1
- package/dist/force-app/main/default/webapplications/appreacttemplateb2e/src/features/object-search/components/FilterPanel.tsx +0 -127
|
@@ -1,41 +1,36 @@
|
|
|
1
1
|
import { Input } from "../../../../components/ui/input";
|
|
2
2
|
import { Label } from "../../../../components/ui/label";
|
|
3
3
|
import { cn } from "../../../../lib/utils";
|
|
4
|
-
import
|
|
4
|
+
import { useFilterField } from "../FilterContext";
|
|
5
|
+
import type { ActiveFilterValue } from "../../utils/filterUtils";
|
|
5
6
|
|
|
6
7
|
interface TextFilterProps extends Omit<React.ComponentProps<"div">, "onChange"> {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
inputProps?: Omit<React.ComponentProps<typeof TextFilterInput>, "config" | "value" | "onChange">;
|
|
12
|
-
helpTextProps?: React.ComponentProps<"p">;
|
|
8
|
+
field: string;
|
|
9
|
+
label: string;
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
helpText?: string;
|
|
13
12
|
}
|
|
14
13
|
|
|
15
14
|
export function TextFilter({
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
field,
|
|
16
|
+
label,
|
|
17
|
+
placeholder,
|
|
18
|
+
helpText,
|
|
19
19
|
className,
|
|
20
|
-
labelProps,
|
|
21
|
-
inputProps,
|
|
22
|
-
helpTextProps,
|
|
23
20
|
...props
|
|
24
21
|
}: TextFilterProps) {
|
|
22
|
+
const { value, onChange } = useFilterField(field);
|
|
25
23
|
return (
|
|
26
24
|
<div className={cn("space-y-1.5", className)} {...props}>
|
|
27
|
-
<Label htmlFor={`filter-${
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
{helpTextProps?.children ?? config.helpText}
|
|
37
|
-
</p>
|
|
38
|
-
)}
|
|
25
|
+
<Label htmlFor={`filter-${field}`}>{label}</Label>
|
|
26
|
+
<TextFilterInput
|
|
27
|
+
field={field}
|
|
28
|
+
label={label}
|
|
29
|
+
placeholder={placeholder}
|
|
30
|
+
value={value}
|
|
31
|
+
onChange={onChange}
|
|
32
|
+
/>
|
|
33
|
+
{helpText && <p className="text-xs text-muted-foreground">{helpText}</p>}
|
|
39
34
|
</div>
|
|
40
35
|
);
|
|
41
36
|
}
|
|
@@ -44,13 +39,15 @@ interface TextFilterInputProps extends Omit<
|
|
|
44
39
|
React.ComponentProps<typeof Input>,
|
|
45
40
|
"onChange" | "value"
|
|
46
41
|
> {
|
|
47
|
-
|
|
42
|
+
field: string;
|
|
43
|
+
label: string;
|
|
48
44
|
value: ActiveFilterValue | undefined;
|
|
49
45
|
onChange: (value: ActiveFilterValue | undefined) => void;
|
|
50
46
|
}
|
|
51
47
|
|
|
52
48
|
export function TextFilterInput({
|
|
53
|
-
|
|
49
|
+
field,
|
|
50
|
+
label,
|
|
54
51
|
value,
|
|
55
52
|
onChange,
|
|
56
53
|
className,
|
|
@@ -58,14 +55,14 @@ export function TextFilterInput({
|
|
|
58
55
|
}: TextFilterInputProps) {
|
|
59
56
|
return (
|
|
60
57
|
<Input
|
|
61
|
-
id={`filter-${
|
|
58
|
+
id={`filter-${field}`}
|
|
62
59
|
type="text"
|
|
63
|
-
placeholder={
|
|
60
|
+
placeholder={props.placeholder ?? `Filter by ${label.toLowerCase()}...`}
|
|
64
61
|
value={value?.value ?? ""}
|
|
65
62
|
onChange={(e) => {
|
|
66
63
|
const v = e.target.value;
|
|
67
64
|
if (v) {
|
|
68
|
-
onChange({ field
|
|
65
|
+
onChange({ field, label, type: "text", value: v });
|
|
69
66
|
} else {
|
|
70
67
|
onChange(undefined);
|
|
71
68
|
}
|
|
@@ -14,6 +14,28 @@ export interface PaginationConfig {
|
|
|
14
14
|
validPageSizes: number[];
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
export interface UseObjectSearchParamsReturn<TFilter, TOrderBy> {
|
|
18
|
+
filters: {
|
|
19
|
+
active: ActiveFilterValue[];
|
|
20
|
+
set: (field: string, value: ActiveFilterValue | undefined) => void;
|
|
21
|
+
remove: (field: string) => void;
|
|
22
|
+
};
|
|
23
|
+
sort: {
|
|
24
|
+
current: SortState | null;
|
|
25
|
+
set: (sort: SortState | null) => void;
|
|
26
|
+
};
|
|
27
|
+
query: { where: TFilter; orderBy: TOrderBy };
|
|
28
|
+
pagination: {
|
|
29
|
+
pageSize: number;
|
|
30
|
+
pageIndex: number;
|
|
31
|
+
afterCursor: string | undefined;
|
|
32
|
+
setPageSize: (size: number) => void;
|
|
33
|
+
goToNextPage: (cursor: string) => void;
|
|
34
|
+
goToPreviousPage: () => void;
|
|
35
|
+
};
|
|
36
|
+
resetAll: () => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
17
39
|
/**
|
|
18
40
|
* Manages filter, sort, and cursor-based pagination state for an object search page.
|
|
19
41
|
*
|
package/dist/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/webapp-template-base-sfdx-project-experimental",
|
|
3
|
-
"version": "1.112.
|
|
3
|
+
"version": "1.112.8",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@salesforce/webapp-template-base-sfdx-project-experimental",
|
|
9
|
-
"version": "1.112.
|
|
9
|
+
"version": "1.112.8",
|
|
10
10
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
11
11
|
"devDependencies": {
|
|
12
12
|
"@lwc/eslint-plugin-lwc": "^3.3.0",
|
package/dist/package.json
CHANGED
package/package.json
CHANGED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { Card, CardContent, CardHeader, CardTitle } from "../../../components/ui/card";
|
|
2
|
-
import { Button } from "../../../components/ui/button";
|
|
3
|
-
import {
|
|
4
|
-
Collapsible,
|
|
5
|
-
CollapsibleContent,
|
|
6
|
-
CollapsibleTrigger,
|
|
7
|
-
} from "../../../components/ui/collapsible";
|
|
8
|
-
import { cn } from "../../../lib/utils";
|
|
9
|
-
import { ChevronDown } from "lucide-react";
|
|
10
|
-
import { useState } from "react";
|
|
11
|
-
import type { FilterFieldConfig, ActiveFilterValue } from "../utils/filterUtils";
|
|
12
|
-
import { TextFilter } from "./filters/TextFilter";
|
|
13
|
-
import { SelectFilter } from "./filters/SelectFilter";
|
|
14
|
-
import { NumericRangeFilter } from "./filters/NumericRangeFilter";
|
|
15
|
-
import { BooleanFilter } from "./filters/BooleanFilter";
|
|
16
|
-
import { DateFilter } from "./filters/DateFilter";
|
|
17
|
-
import { DateRangeFilter } from "./filters/DateRangeFilter";
|
|
18
|
-
import { MultiSelectFilter } from "./filters/MultiSelectFilter";
|
|
19
|
-
import { SearchFilter } from "./filters/SearchFilter";
|
|
20
|
-
|
|
21
|
-
interface FilterPanelProps extends Omit<React.ComponentProps<"div">, "onReset"> {
|
|
22
|
-
configs: FilterFieldConfig[];
|
|
23
|
-
filters: ActiveFilterValue[];
|
|
24
|
-
onFilterChange: (field: string, value: ActiveFilterValue | undefined) => void;
|
|
25
|
-
onReset: () => void;
|
|
26
|
-
headerProps?: React.ComponentProps<typeof CardHeader>;
|
|
27
|
-
titleProps?: React.ComponentProps<typeof CardTitle>;
|
|
28
|
-
contentProps?: React.ComponentProps<typeof CardContent>;
|
|
29
|
-
resetButtonProps?: React.ComponentProps<typeof Button>;
|
|
30
|
-
toggleButtonProps?: React.ComponentProps<typeof Button>;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function getFilterValue(
|
|
34
|
-
filters: ActiveFilterValue[],
|
|
35
|
-
field: string,
|
|
36
|
-
): ActiveFilterValue | undefined {
|
|
37
|
-
return filters.find((f) => f.field === field);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function renderFilter(
|
|
41
|
-
config: FilterFieldConfig,
|
|
42
|
-
value: ActiveFilterValue | undefined,
|
|
43
|
-
onChange: (value: ActiveFilterValue | undefined) => void,
|
|
44
|
-
) {
|
|
45
|
-
switch (config.type) {
|
|
46
|
-
case "text":
|
|
47
|
-
return <TextFilter config={config} value={value} onChange={onChange} />;
|
|
48
|
-
case "picklist":
|
|
49
|
-
return <SelectFilter config={config} value={value} onChange={onChange} />;
|
|
50
|
-
case "numeric":
|
|
51
|
-
return <NumericRangeFilter config={config} value={value} onChange={onChange} />;
|
|
52
|
-
case "boolean":
|
|
53
|
-
return <BooleanFilter config={config} value={value} onChange={onChange} />;
|
|
54
|
-
case "date":
|
|
55
|
-
return <DateFilter config={config} value={value} onChange={onChange} />;
|
|
56
|
-
case "daterange":
|
|
57
|
-
return <DateRangeFilter config={config} value={value} onChange={onChange} />;
|
|
58
|
-
case "multipicklist":
|
|
59
|
-
return <MultiSelectFilter config={config} value={value} onChange={onChange} />;
|
|
60
|
-
case "search":
|
|
61
|
-
return <SearchFilter config={config} value={value} onChange={onChange} />;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function FilterPanel({
|
|
66
|
-
configs,
|
|
67
|
-
filters,
|
|
68
|
-
onFilterChange,
|
|
69
|
-
onReset,
|
|
70
|
-
className,
|
|
71
|
-
headerProps,
|
|
72
|
-
titleProps,
|
|
73
|
-
contentProps,
|
|
74
|
-
resetButtonProps,
|
|
75
|
-
toggleButtonProps,
|
|
76
|
-
...props
|
|
77
|
-
}: FilterPanelProps) {
|
|
78
|
-
const [open, setOpen] = useState(true);
|
|
79
|
-
const hasActiveFilters = filters.length > 0;
|
|
80
|
-
|
|
81
|
-
return (
|
|
82
|
-
<Card className={cn(className)} {...props}>
|
|
83
|
-
<Collapsible open={open} onOpenChange={setOpen}>
|
|
84
|
-
<CardHeader
|
|
85
|
-
{...headerProps}
|
|
86
|
-
className={cn(
|
|
87
|
-
"flex flex-row items-center justify-between space-y-0 pb-2",
|
|
88
|
-
headerProps?.className,
|
|
89
|
-
)}
|
|
90
|
-
>
|
|
91
|
-
<CardTitle
|
|
92
|
-
{...titleProps}
|
|
93
|
-
className={cn("text-base font-semibold", titleProps?.className)}
|
|
94
|
-
>
|
|
95
|
-
{titleProps?.children ?? <h2>Filters</h2>}
|
|
96
|
-
</CardTitle>
|
|
97
|
-
<div className="flex items-center gap-1">
|
|
98
|
-
{hasActiveFilters && (
|
|
99
|
-
<Button variant="destructive" size="sm" onClick={onReset} {...resetButtonProps}>
|
|
100
|
-
{resetButtonProps?.children ?? "Reset"}
|
|
101
|
-
</Button>
|
|
102
|
-
)}
|
|
103
|
-
<CollapsibleTrigger asChild>
|
|
104
|
-
<Button variant="ghost" size="icon" {...toggleButtonProps}>
|
|
105
|
-
<ChevronDown
|
|
106
|
-
className={`h-4 w-4 transition-transform ${open ? "" : "-rotate-90"}`}
|
|
107
|
-
/>
|
|
108
|
-
<span className="sr-only">Toggle filters</span>
|
|
109
|
-
</Button>
|
|
110
|
-
</CollapsibleTrigger>
|
|
111
|
-
</div>
|
|
112
|
-
</CardHeader>
|
|
113
|
-
<CollapsibleContent>
|
|
114
|
-
<CardContent {...contentProps} className={cn("space-y-4 pt-0", contentProps?.className)}>
|
|
115
|
-
{configs.map((config) => (
|
|
116
|
-
<div key={config.field}>
|
|
117
|
-
{renderFilter(config, getFilterValue(filters, config.field), (value) =>
|
|
118
|
-
onFilterChange(config.field, value),
|
|
119
|
-
)}
|
|
120
|
-
</div>
|
|
121
|
-
))}
|
|
122
|
-
</CardContent>
|
|
123
|
-
</CollapsibleContent>
|
|
124
|
-
</Collapsible>
|
|
125
|
-
</Card>
|
|
126
|
-
);
|
|
127
|
-
}
|