@servicetitan/table 23.4.0 → 24.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. package/dist/demo/filters/async-select-filter.d.ts +3 -0
  2. package/dist/demo/filters/async-select-filter.d.ts.map +1 -0
  3. package/dist/demo/filters/async-select-filter.js +23 -0
  4. package/dist/demo/filters/async-select-filter.js.map +1 -0
  5. package/dist/demo/filters/categories.d.ts +9 -0
  6. package/dist/demo/filters/categories.d.ts.map +1 -0
  7. package/dist/demo/filters/categories.js +33 -0
  8. package/dist/demo/filters/categories.js.map +1 -0
  9. package/dist/demo/filters/multiselect-filter.d.ts +3 -0
  10. package/dist/demo/filters/multiselect-filter.d.ts.map +1 -0
  11. package/dist/demo/filters/multiselect-filter.js +14 -0
  12. package/dist/demo/filters/multiselect-filter.js.map +1 -0
  13. package/dist/demo/filters/range-filter.d.ts +5 -0
  14. package/dist/demo/filters/range-filter.d.ts.map +1 -0
  15. package/dist/demo/filters/range-filter.js +24 -0
  16. package/dist/demo/filters/range-filter.js.map +1 -0
  17. package/dist/demo/filters/single-select-filter.d.ts +3 -0
  18. package/dist/demo/filters/single-select-filter.d.ts.map +1 -0
  19. package/dist/demo/filters/single-select-filter.js +20 -0
  20. package/dist/demo/filters/single-select-filter.js.map +1 -0
  21. package/dist/demo/filters/table.store.d.ts +9 -0
  22. package/dist/demo/filters/table.store.d.ts.map +1 -0
  23. package/dist/demo/filters/table.store.js +73 -0
  24. package/dist/demo/filters/table.store.js.map +1 -0
  25. package/dist/demo/overview/products.js +10 -10
  26. package/dist/filters/async-select/async-select-filter.d.ts +19 -0
  27. package/dist/filters/async-select/async-select-filter.d.ts.map +1 -0
  28. package/dist/filters/async-select/async-select-filter.js +168 -0
  29. package/dist/filters/async-select/async-select-filter.js.map +1 -0
  30. package/dist/filters/async-select/async-select-filter.stories.d.ts +7 -0
  31. package/dist/filters/async-select/async-select-filter.stories.d.ts.map +1 -0
  32. package/dist/filters/async-select/async-select-filter.stories.js +7 -0
  33. package/dist/filters/async-select/async-select-filter.stories.js.map +1 -0
  34. package/dist/filters/index.d.ts +3 -1
  35. package/dist/filters/index.d.ts.map +1 -1
  36. package/dist/filters/index.js +3 -1
  37. package/dist/filters/index.js.map +1 -1
  38. package/dist/filters/multiselect-filter/multiselect-filter.d.ts.map +1 -0
  39. package/dist/filters/{multiselect-filter.js → multiselect-filter/multiselect-filter.js} +1 -1
  40. package/dist/filters/multiselect-filter/multiselect-filter.js.map +1 -0
  41. package/dist/filters/multiselect-filter/multiselect-filter.stories.d.ts +7 -0
  42. package/dist/filters/multiselect-filter/multiselect-filter.stories.d.ts.map +1 -0
  43. package/dist/filters/multiselect-filter/multiselect-filter.stories.js +7 -0
  44. package/dist/filters/multiselect-filter/multiselect-filter.stories.js.map +1 -0
  45. package/dist/filters/range-filter/range-filter.stories.d.ts +9 -0
  46. package/dist/filters/range-filter/range-filter.stories.d.ts.map +1 -0
  47. package/dist/filters/range-filter/range-filter.stories.js +9 -0
  48. package/dist/filters/range-filter/range-filter.stories.js.map +1 -0
  49. package/dist/filters/single-select/single-select-filter.d.ts +9 -0
  50. package/dist/filters/single-select/single-select-filter.d.ts.map +1 -0
  51. package/dist/filters/single-select/single-select-filter.js +30 -0
  52. package/dist/filters/single-select/single-select-filter.js.map +1 -0
  53. package/dist/filters/single-select/single-select-filter.stories.d.ts +7 -0
  54. package/dist/filters/single-select/single-select-filter.stories.d.ts.map +1 -0
  55. package/dist/filters/single-select/single-select-filter.stories.js +7 -0
  56. package/dist/filters/single-select/single-select-filter.stories.js.map +1 -0
  57. package/dist/index.d.ts +1 -0
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +1 -0
  60. package/dist/index.js.map +1 -1
  61. package/dist/utils/filters.d.ts +4 -0
  62. package/dist/utils/filters.d.ts.map +1 -0
  63. package/dist/utils/filters.js +22 -0
  64. package/dist/utils/filters.js.map +1 -0
  65. package/package.json +6 -6
  66. package/src/demo/filters/async-select-filter.tsx +53 -0
  67. package/src/demo/filters/categories.tsx +40 -0
  68. package/src/demo/filters/multiselect-filter.tsx +32 -0
  69. package/src/demo/filters/range-filter.tsx +72 -0
  70. package/src/demo/filters/single-select-filter.tsx +51 -0
  71. package/src/demo/filters/table.store.ts +45 -0
  72. package/src/demo/overview/products.ts +10 -10
  73. package/src/filters/async-select/async-select-filter.stories.tsx +7 -0
  74. package/src/filters/async-select/async-select-filter.tsx +249 -0
  75. package/src/filters/index.ts +3 -1
  76. package/src/filters/multiselect-filter/multiselect-filter.stories.tsx +7 -0
  77. package/src/filters/{multiselect-filter.tsx → multiselect-filter/multiselect-filter.tsx} +1 -1
  78. package/src/filters/range-filter/range-filter.stories.tsx +13 -0
  79. package/src/filters/single-select/single-select-filter.stories.tsx +7 -0
  80. package/src/filters/single-select/single-select-filter.tsx +66 -0
  81. package/src/index.ts +1 -0
  82. package/src/utils/__tests__/filters.test.ts +31 -0
  83. package/src/utils/filters.ts +33 -0
  84. package/dist/filters/multiselect-filter.d.ts.map +0 -1
  85. package/dist/filters/multiselect-filter.js.map +0 -1
  86. /package/dist/filters/{multiselect-filter.d.ts → multiselect-filter/multiselect-filter.d.ts} +0 -0
@@ -10,7 +10,7 @@ export const products: Product[] = [
10
10
  QuantityPerUnit: '10 boxes x 20 bags',
11
11
  UnitPrice: 18.0,
12
12
  UnitsInStock: 39,
13
- UnitsOnOrder: new Date('1/11/2019'),
13
+ UnitsOnOrder: new Date('1/11/2023'),
14
14
  Discontinued: false,
15
15
  AvailableFor: UserRole.Admin,
16
16
  },
@@ -23,7 +23,7 @@ export const products: Product[] = [
23
23
  QuantityPerUnit: '24 - 12 oz bottles',
24
24
  UnitPrice: 19.0,
25
25
  UnitsInStock: 17,
26
- UnitsOnOrder: new Date('2/11/2019'),
26
+ UnitsOnOrder: new Date('2/11/2023'),
27
27
  Discontinued: false,
28
28
  },
29
29
  {
@@ -35,7 +35,7 @@ export const products: Product[] = [
35
35
  QuantityPerUnit: '12 - 550 ml bottles',
36
36
  UnitPrice: 10.0,
37
37
  UnitsInStock: 13,
38
- UnitsOnOrder: new Date('3/11/2019'),
38
+ UnitsOnOrder: new Date('3/11/2023'),
39
39
  Discontinued: false,
40
40
  AvailableFor: UserRole.Owner,
41
41
  },
@@ -48,7 +48,7 @@ export const products: Product[] = [
48
48
  QuantityPerUnit: '48 - 6 oz jars',
49
49
  UnitPrice: 22.0,
50
50
  UnitsInStock: 53,
51
- UnitsOnOrder: new Date('4/11/2019'),
51
+ UnitsOnOrder: new Date('4/11/2023'),
52
52
  Discontinued: false,
53
53
  },
54
54
  {
@@ -60,7 +60,7 @@ export const products: Product[] = [
60
60
  QuantityPerUnit: '36 boxes',
61
61
  UnitPrice: 21.35,
62
62
  UnitsInStock: 0,
63
- UnitsOnOrder: new Date('5/11/2019'),
63
+ UnitsOnOrder: new Date('5/11/2023'),
64
64
  Discontinued: true,
65
65
  AvailableFor: UserRole.GeneralOffice,
66
66
  },
@@ -73,7 +73,7 @@ export const products: Product[] = [
73
73
  QuantityPerUnit: '12 - 8 oz jars',
74
74
  UnitPrice: 25.0,
75
75
  UnitsInStock: 120,
76
- UnitsOnOrder: new Date('6/11/2019'),
76
+ UnitsOnOrder: new Date('6/11/2023'),
77
77
  Discontinued: false,
78
78
  },
79
79
  {
@@ -85,7 +85,7 @@ export const products: Product[] = [
85
85
  QuantityPerUnit: '12 - 1 lb pkgs.',
86
86
  UnitPrice: 30.0,
87
87
  UnitsInStock: 15,
88
- UnitsOnOrder: new Date('7/11/2019'),
88
+ UnitsOnOrder: new Date('7/11/2023'),
89
89
  Discontinued: false,
90
90
  },
91
91
  {
@@ -97,7 +97,7 @@ export const products: Product[] = [
97
97
  QuantityPerUnit: '12 - 12 oz jars',
98
98
  UnitPrice: 40.0,
99
99
  UnitsInStock: 6,
100
- UnitsOnOrder: new Date('8/11/2019'),
100
+ UnitsOnOrder: new Date('8/11/2023'),
101
101
  Discontinued: false,
102
102
  },
103
103
  {
@@ -109,7 +109,7 @@ export const products: Product[] = [
109
109
  QuantityPerUnit: '18 - 500 g pkgs.',
110
110
  UnitPrice: 97.0,
111
111
  UnitsInStock: 29,
112
- UnitsOnOrder: new Date('9/11/2019'),
112
+ UnitsOnOrder: new Date('9/11/2023'),
113
113
  Discontinued: true,
114
114
  },
115
115
  {
@@ -121,7 +121,7 @@ export const products: Product[] = [
121
121
  QuantityPerUnit: '12 - 200 ml jars',
122
122
  UnitPrice: 31.0,
123
123
  UnitsInStock: 31,
124
- UnitsOnOrder: new Date('10/11/2019'),
124
+ UnitsOnOrder: new Date('10/11/2023'),
125
125
  Discontinued: false,
126
126
  },
127
127
  ];
@@ -0,0 +1,7 @@
1
+ import { TableExample } from '../../demo/filters/async-select-filter';
2
+
3
+ export default {
4
+ title: 'Table/Filters',
5
+ };
6
+
7
+ export const AsyncSelect = () => <TableExample />;
@@ -0,0 +1,249 @@
1
+ import { Component, SyntheticEvent, ReactNode, Fragment, KeyboardEvent, FC } from 'react';
2
+
3
+ import {
4
+ Checkbox,
5
+ TableFilterCellProps,
6
+ Radio,
7
+ Input,
8
+ Spinner,
9
+ BodyText,
10
+ } from '@servicetitan/design-system';
11
+ import { IdType } from '@servicetitan/data-query';
12
+
13
+ import { renderCustomColumnMenuFilter } from '../column-menu-filters';
14
+ import { makeObservable, observable, runInAction } from 'mobx';
15
+ import { observer } from 'mobx-react';
16
+
17
+ export type AsyncSelectFilterDataFetcher<
18
+ TV extends IdType = IdType,
19
+ TO extends AsyncSelectItem<TV> = AsyncSelectItem<TV>
20
+ > = (opts: { search?: string }) => Promise<{ data: TO[] }>;
21
+
22
+ export interface AsyncSelectItem<TV extends IdType = IdType> {
23
+ value: TV;
24
+ text: string;
25
+ }
26
+
27
+ interface SelectorProps<TID extends IdType, TO extends AsyncSelectItem<TID>> {
28
+ placeholder: string;
29
+ selected: TO[];
30
+ dataFetcher: AsyncSelectFilterDataFetcher<TID, TO>;
31
+ itemComponent: FC<SelectorItemProps>;
32
+ onChange(option: TO, checked: boolean, event: SyntheticEvent<HTMLInputElement>): void;
33
+ renderer?(item: TO): ReactNode;
34
+ }
35
+
36
+ interface SelectorItemProps {
37
+ option: AsyncSelectItem;
38
+ checked: boolean;
39
+ onChange(
40
+ option: AsyncSelectItem,
41
+ checked: boolean,
42
+ event: SyntheticEvent<HTMLInputElement>
43
+ ): void;
44
+ renderer?(item: AsyncSelectItem): ReactNode;
45
+ }
46
+
47
+ @observer
48
+ class SelectorAsync<TID extends IdType, TO extends AsyncSelectItem<TID>> extends Component<
49
+ SelectorProps<TID, TO>
50
+ > {
51
+ @observable shownOptions: AsyncSelectItem<TID>[] = [];
52
+ @observable search = '';
53
+ @observable error = false;
54
+ @observable loading = false;
55
+
56
+ constructor(props: SelectorProps<TID, TO>) {
57
+ super(props);
58
+ makeObservable(this);
59
+ }
60
+
61
+ componentDidMount() {
62
+ this.searchOptions().catch();
63
+ }
64
+
65
+ handleKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
66
+ if (event.key === 'Enter') {
67
+ event.stopPropagation();
68
+ }
69
+ };
70
+
71
+ handleSearch = (_0: SyntheticEvent<HTMLInputElement>, data: { value: string }) => {
72
+ runInAction(() => (this.search = data.value));
73
+ this.searchOptions().catch();
74
+ };
75
+
76
+ searchOptions = async () => {
77
+ runInAction(() => {
78
+ this.loading = true;
79
+ });
80
+
81
+ try {
82
+ const { data } = await this.props.dataFetcher({
83
+ search: this.search,
84
+ });
85
+
86
+ runInAction(() => {
87
+ this.shownOptions = data;
88
+ this.error = false;
89
+ this.loading = false;
90
+ });
91
+ } catch {
92
+ runInAction(() => {
93
+ this.error = true;
94
+ this.loading = false;
95
+ });
96
+ }
97
+ };
98
+
99
+ render() {
100
+ const selectedOptions = this.props.selected;
101
+ const selected = new Set(selectedOptions.map(opt => opt.value));
102
+ const ItemComponent = this.props.itemComponent;
103
+
104
+ return (
105
+ <Fragment>
106
+ <Input
107
+ className="m-x-half m-t-half m-b-2"
108
+ placeholder={this.props.placeholder}
109
+ onChange={this.handleSearch}
110
+ onKeyDown={this.handleKeyDown}
111
+ size="xsmall"
112
+ value={this.search}
113
+ />
114
+ <div className="p-x-half position-relative" onKeyDown={this.handleKeyDown}>
115
+ {!!selectedOptions.length && (
116
+ <div className="border-bottom m-y-half">
117
+ {selectedOptions.map(option => (
118
+ <ItemComponent
119
+ key={option.value}
120
+ option={option}
121
+ checked
122
+ renderer={this.props.renderer}
123
+ onChange={this.props.onChange}
124
+ />
125
+ ))}
126
+ </div>
127
+ )}
128
+ {this.error ? (
129
+ <BodyText className="c-red-500">Unable to load options</BodyText>
130
+ ) : this.shownOptions.length ? (
131
+ this.shownOptions
132
+ .filter(opt => !selected.has(opt.value))
133
+ .map(option => (
134
+ <ItemComponent
135
+ key={option.value}
136
+ option={option}
137
+ checked={false}
138
+ renderer={this.props.renderer}
139
+ onChange={this.props.onChange}
140
+ />
141
+ ))
142
+ ) : this.loading ? (
143
+ <BodyText>&nbsp;</BodyText>
144
+ ) : (
145
+ <BodyText>No options found</BodyText>
146
+ )}
147
+
148
+ {this.loading && (
149
+ <div className="position-absolute top-0 bottom-0 left-0 right-0 opacity-disabled bg-white d-f justify-content-center">
150
+ <Spinner size="tiny" />
151
+ </div>
152
+ )}
153
+ </div>
154
+ </Fragment>
155
+ );
156
+ }
157
+ }
158
+
159
+ const SelectorItemSingle: FC<SelectorItemProps> = ({ option, renderer, checked, onChange }) => (
160
+ <Radio
161
+ label={renderer?.(option) ?? option.text}
162
+ checked={checked}
163
+ onChange={(_, event) => onChange(option, true, event)}
164
+ className="m-b-1"
165
+ />
166
+ );
167
+
168
+ const SelectorItemMultiple: FC<SelectorItemProps> = ({ option, renderer, checked, onChange }) => (
169
+ <Checkbox
170
+ label={renderer?.(option) ?? option.text}
171
+ checked={checked}
172
+ onChange={(_, checked, event) => onChange(option, checked, event)}
173
+ className="m-b-1"
174
+ />
175
+ );
176
+
177
+ export interface AsyncSelectFilterOptions<TID extends IdType, TO extends AsyncSelectItem<TID>> {
178
+ dataFetcher: AsyncSelectFilterDataFetcher<TID, TO>;
179
+ placeholder: string;
180
+ multiple?: boolean;
181
+ renderItem?: (item: TO) => ReactNode;
182
+ }
183
+
184
+ interface TableFilterCellPropsTyped<T = any> extends Omit<TableFilterCellProps, 'value'> {
185
+ value?: T;
186
+ }
187
+
188
+ export function asyncSelectColumnMenuFilter<TID extends IdType, TO extends AsyncSelectItem<TID>>(
189
+ options: AsyncSelectFilterOptions<TID, TO>
190
+ ) {
191
+ const contains = (value: TID, options?: TO[]) => options?.some(opt => opt.value === value);
192
+ const equals = (value: TID, option?: TO) => option?.value === value;
193
+
194
+ const FilterCell = options.multiple
195
+ ? ({ value, onChange }: TableFilterCellPropsTyped<TO[]>) => {
196
+ const handleChange = (
197
+ option: TO,
198
+ checked: boolean,
199
+ event: SyntheticEvent<HTMLInputElement>
200
+ ) => {
201
+ const val = checked
202
+ ? (value ?? []).concat(option)
203
+ : (value ?? []).filter(opt => opt.value !== option.value);
204
+
205
+ onChange({
206
+ value: val.length ? val : undefined,
207
+ operator: val.length ? contains : '',
208
+ syntheticEvent: event,
209
+ });
210
+ };
211
+
212
+ return (
213
+ <SelectorAsync
214
+ selected={value ?? []}
215
+ itemComponent={SelectorItemMultiple}
216
+ onChange={handleChange}
217
+ placeholder={options.placeholder}
218
+ renderer={options.renderItem}
219
+ dataFetcher={options.dataFetcher}
220
+ />
221
+ );
222
+ }
223
+ : ({ value, onChange }: TableFilterCellPropsTyped<TO>) => {
224
+ const handleChange = (
225
+ option: TO,
226
+ checked: boolean,
227
+ event: SyntheticEvent<HTMLInputElement>
228
+ ) => {
229
+ onChange({
230
+ value: option,
231
+ operator: option ? equals : '',
232
+ syntheticEvent: event,
233
+ });
234
+ };
235
+
236
+ return (
237
+ <SelectorAsync
238
+ selected={value ? [value] : []}
239
+ onChange={handleChange}
240
+ itemComponent={SelectorItemSingle}
241
+ placeholder={options.placeholder}
242
+ renderer={options.renderItem}
243
+ dataFetcher={options.dataFetcher}
244
+ />
245
+ );
246
+ };
247
+
248
+ return renderCustomColumnMenuFilter(FilterCell);
249
+ }
@@ -4,5 +4,7 @@ export * from './standard-filter-with-multiselect/standard-filter-with-multisele
4
4
  export * from './time-filter/time-filter';
5
5
  export * from './column-menu-filters';
6
6
  export * from './field-values-filter';
7
- export * from './multiselect-filter';
7
+ export * from './multiselect-filter/multiselect-filter';
8
8
  export * from './numeric-filter-extended/numeric-filter-extended';
9
+ export * from './single-select/single-select-filter';
10
+ export * from './async-select/async-select-filter';
@@ -0,0 +1,7 @@
1
+ import { TableExampleSimple } from '../../demo/filters/multiselect-filter';
2
+
3
+ export default {
4
+ title: 'Table/Filters/MultiSelect',
5
+ };
6
+
7
+ export const DefaultMultiSelect = () => <TableExampleSimple />;
@@ -2,7 +2,7 @@ import { Component, SyntheticEvent, ReactNode, FC } from 'react';
2
2
 
3
3
  import { TableFilterCellProps, Checkbox } from '@servicetitan/design-system';
4
4
 
5
- import { renderCustomColumnMenuFilter } from './column-menu-filters';
5
+ import { renderCustomColumnMenuFilter } from '../column-menu-filters';
6
6
 
7
7
  interface SelectorProps<T> {
8
8
  options: T[];
@@ -0,0 +1,13 @@
1
+ import {
2
+ TableExampleCurrency,
3
+ TableExampleDate,
4
+ TableExampleNumber,
5
+ } from '../../demo/filters/range-filter';
6
+
7
+ export default {
8
+ title: 'Table/Filters/Range',
9
+ };
10
+
11
+ export const DateRange = () => <TableExampleDate />;
12
+ export const NumericRange = () => <TableExampleNumber />;
13
+ export const CurrencyRange = () => <TableExampleCurrency />;
@@ -0,0 +1,7 @@
1
+ import { TableExample } from '../../demo/filters/single-select-filter';
2
+
3
+ export default {
4
+ title: 'Table/Filters',
5
+ };
6
+
7
+ export const SingleSelect = () => <TableExample />;
@@ -0,0 +1,66 @@
1
+ import { Component, SyntheticEvent, ReactNode, FC } from 'react';
2
+
3
+ import { TableFilterCellProps, Radio } from '@servicetitan/design-system';
4
+
5
+ import { renderCustomColumnMenuFilter } from '../column-menu-filters';
6
+ import { IdType } from '@servicetitan/data-query';
7
+
8
+ interface SelectorProps<TV, TO> {
9
+ options: TO[];
10
+ value?: TV;
11
+ valueSelector(item: TO): TV;
12
+ onChange(value: TV | undefined, event: SyntheticEvent<HTMLInputElement>): void;
13
+ renderer?(item: TO): ReactNode;
14
+ }
15
+
16
+ class SelectorRadio<TV, TO> extends Component<SelectorProps<TV, TO>> {
17
+ render() {
18
+ const { options, value, renderer } = this.props;
19
+
20
+ return options.map((option, index) => {
21
+ const optionValue = this.props.valueSelector(option);
22
+ return (
23
+ // eslint-disable-next-line react/no-array-index-key
24
+ <span key={index} className="k-widget m-b-1-i d-b">
25
+ <Radio
26
+ label={renderer ? renderer(option) : `${option}`}
27
+ checked={value === optionValue}
28
+ onChange={(_, event) => this.props.onChange(optionValue, event)}
29
+ />
30
+ </span>
31
+ );
32
+ });
33
+ }
34
+ }
35
+
36
+ export interface SingleSelectColumnMenuOptions<TV, TO = TV> {
37
+ options: TO[];
38
+ valueSelector?: (item: TO) => TV;
39
+ renderItem?: (item: TO) => ReactNode | string;
40
+ }
41
+
42
+ export function singleSelectColumnMenuFilter<TV extends IdType, TO = TV>(
43
+ props: SingleSelectColumnMenuOptions<TV, TO>
44
+ ) {
45
+ const FilterCell: FC<TableFilterCellProps> = ({ value, onChange }) => {
46
+ const handleChange = (value: TV | undefined, event: SyntheticEvent<HTMLInputElement>) => {
47
+ onChange({
48
+ value: value ?? '',
49
+ operator: value ? 'equals' : '',
50
+ syntheticEvent: event,
51
+ });
52
+ };
53
+
54
+ return (
55
+ <SelectorRadio
56
+ options={props.options}
57
+ value={value === '' ? undefined : value}
58
+ onChange={handleChange}
59
+ renderer={props.renderItem}
60
+ valueSelector={props.valueSelector ?? (option => option)}
61
+ />
62
+ );
63
+ };
64
+
65
+ return renderCustomColumnMenuFilter(FilterCell);
66
+ }
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@ export * from './filters';
5
5
  export * from './export';
6
6
  export * from './select-cell';
7
7
  export * from './use-observing-table-state';
8
+ export * from './utils/filters';
8
9
 
9
10
  export {
10
11
  TableColumn,
@@ -0,0 +1,31 @@
1
+ import { getFiltersFlat, getFiltersMap } from '../filters';
2
+ import { CompositeFilterDescriptor, FilterDescriptor } from '@servicetitan/data-query';
3
+
4
+ const filter = (field: string) => ({ field, operator: 'qq', value: 'ee' });
5
+ const composite = (
6
+ filters: (FilterDescriptor | CompositeFilterDescriptor)[]
7
+ ): CompositeFilterDescriptor => ({ filters, logic: 'and' });
8
+
9
+ const filters1 = () =>
10
+ composite([composite([filter('f1'), filter('f2')]), filter('f3'), composite([filter('f1')])]);
11
+
12
+ describe('getFiltersFlat', () => {
13
+ test('getFiltersFlat', () => {
14
+ expect(getFiltersFlat(filters1())).toEqual([
15
+ filter('f1'),
16
+ filter('f2'),
17
+ filter('f3'),
18
+ filter('f1'),
19
+ ]);
20
+ });
21
+ });
22
+
23
+ describe('getFiltersMap', () => {
24
+ test('getFiltersMap', () => {
25
+ expect(getFiltersMap(filters1())).toEqual({
26
+ f1: filter('f1'),
27
+ f2: filter('f2'),
28
+ f3: filter('f3'),
29
+ });
30
+ });
31
+ });
@@ -0,0 +1,33 @@
1
+ import {
2
+ CompositeFilterDescriptor,
3
+ FilterDescriptor,
4
+ isCompositeFilterDescriptor,
5
+ State,
6
+ } from '@servicetitan/data-query';
7
+
8
+ const getFiltersFlatInner = (
9
+ filter?: FilterDescriptor | CompositeFilterDescriptor
10
+ ): FilterDescriptor[] => {
11
+ if (!filter) {
12
+ return [];
13
+ } else if (isCompositeFilterDescriptor(filter)) {
14
+ return filter.filters.reduce(
15
+ (out, item) => [...out, ...getFiltersFlatInner(item)],
16
+ [] as FilterDescriptor[]
17
+ );
18
+ }
19
+ return [filter];
20
+ };
21
+
22
+ export const getFiltersFlat = (filter: State['filter']): FilterDescriptor[] => {
23
+ return getFiltersFlatInner(filter);
24
+ };
25
+
26
+ export const getFiltersMap = (filter: State['filter']): Record<string, FilterDescriptor> => {
27
+ return getFiltersFlatInner(filter).reduce((out, filter) => {
28
+ if (typeof filter.field === 'string') {
29
+ out[filter.field] = filter;
30
+ }
31
+ return out;
32
+ }, {} as Record<string, FilterDescriptor>);
33
+ };
@@ -1 +0,0 @@
1
- {"version":3,"file":"multiselect-filter.d.ts","sourceRoot":"","sources":["../../src/filters/multiselect-filter.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6B,SAAS,EAAM,MAAM,OAAO,CAAC;AAuCjE,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,SAAS,sFAqB5F;AAED,wBAAgB,sCAAsC,CAAC,CAAC,EACpD,IAAI,EAAE,CAAC,EAAE,EACT,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,SAAS,sFAgCtC;AAED,wBAAgB,oCAAoC,CAAC,CAAC,EAClD,IAAI,EAAE,CAAC,EAAE,EACT,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,SAAS,sFAgCtC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"multiselect-filter.js","sourceRoot":"","sources":["../../src/filters/multiselect-filter.tsx"],"names":[],"mappings":"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"}