@orchestrator-ui/orchestrator-ui-components 8.5.0 → 8.6.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.
Files changed (30) hide show
  1. package/.turbo/turbo-build.log +10 -14
  2. package/.turbo/turbo-lint.log +2 -4
  3. package/.turbo/turbo-test.log +7 -9
  4. package/CHANGELOG.md +6 -0
  5. package/dist/index.d.ts +84 -74
  6. package/dist/index.js +2568 -2252
  7. package/dist/index.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/components/WfoAutoExpandableTextArea/WfoAutoExpandableTextArea.tsx +54 -0
  10. package/src/components/WfoAutoExpandableTextArea/index.ts +1 -0
  11. package/src/components/WfoAutoExpandableTextArea/styles.ts +18 -0
  12. package/src/components/WfoPageTemplate/paths.ts +1 -0
  13. package/src/components/WfoTable/WfoStructuredSearchTable/WfoAddGroupAction.tsx +1 -1
  14. package/src/components/WfoTable/WfoStructuredSearchTable/WfoCombinatorSelector.tsx +3 -0
  15. package/src/components/WfoTable/WfoStructuredSearchTable/WfoFieldSelector.tsx +58 -4
  16. package/src/components/WfoTable/WfoStructuredSearchTable/WfoFilterBuilder.tsx +37 -28
  17. package/src/components/WfoTable/WfoStructuredSearchTable/WfoInlineCombinator.tsx +17 -0
  18. package/src/components/WfoTable/WfoStructuredSearchTable/WfoRemoveRuleAction.tsx +3 -3
  19. package/src/components/WfoTable/WfoStructuredSearchTable/WfoRuleGroup.tsx +6 -3
  20. package/src/components/WfoTable/WfoStructuredSearchTable/WfoSearchFieldWithActions.tsx +56 -0
  21. package/src/components/WfoTable/WfoStructuredSearchTable/WfoStructuredSearchTable.tsx +84 -81
  22. package/src/components/WfoTable/WfoStructuredSearchTable/styles.ts +52 -16
  23. package/src/components/WfoTable/WfoStructuredSearchTable/utils.ts +45 -0
  24. package/src/components/WfoTable/WfoTableSettingsModal/WfoTableSettingsModal.tsx +9 -0
  25. package/src/components/index.ts +1 -0
  26. package/src/configuration/version.ts +1 -1
  27. package/src/messages/en-GB.json +1 -0
  28. package/src/messages/nl-NL.json +1 -0
  29. package/src/pages/WfoSearchPocPage.tsx +151 -25
  30. package/src/types/search.ts +3 -0
@@ -3,17 +3,7 @@ import type { RuleGroupType } from 'react-querybuilder';
3
3
 
4
4
  import { useTranslations } from 'next-intl';
5
5
 
6
- import {
7
- EuiButton,
8
- EuiButtonIcon,
9
- EuiFieldSearch,
10
- EuiFlexGroup,
11
- EuiFlexItem,
12
- EuiFormRow,
13
- EuiSelect,
14
- EuiSpacer,
15
- EuiText,
16
- } from '@elastic/eui';
6
+ import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSelect, EuiSpacer, EuiText } from '@elastic/eui';
17
7
 
18
8
  import {
19
9
  DEFAULT_PAGE_SIZE,
@@ -22,6 +12,7 @@ import {
22
12
  TableSettingsColumnConfig,
23
13
  TableSettingsConfig,
24
14
  TableSettingsModal,
15
+ WfoDataSearch,
25
16
  WfoDataSorting,
26
17
  WfoErrorWithMessage,
27
18
  WfoInformationModal,
@@ -36,15 +27,16 @@ import {
36
27
  WfoTableControlColumnConfigItem,
37
28
  WfoTableDataColumnConfigItem,
38
29
  } from '@/components/WfoTable/WfoTable';
39
- import { useOrchestratorTheme, useWithOrchestratorTheme } from '@/hooks';
30
+ import { useOrchestratorTheme } from '@/hooks';
40
31
  import { WfoArrowsExpand } from '@/icons';
41
32
  import { WfoGraphqlError } from '@/rtk';
42
- import { getFormFieldsBaseStyle } from '@/theme';
43
- import { RetrieverType } from '@/types';
33
+ import { FieldToOperatorMap, RetrieverType } from '@/types';
44
34
  import { getDefaultTableConfig } from '@/utils';
45
35
 
46
36
  import { ColumnType, WfoTable, WfoTableProps } from '../WfoTable';
47
37
  import { WfoFilterBuilder } from './WfoFilterBuilder';
38
+ import { WfoSearchFieldWithActions } from './WfoSearchFieldWithActions';
39
+ import { buildColumnFilter } from './utils';
48
40
 
49
41
  export type WfoStructuredSearchTableDataColumnConfigItem<
50
42
  T extends object,
@@ -85,6 +77,9 @@ export type WfoStructuredSearchTableProps<T extends object> = Omit<
85
77
  onSearchQueryText: (queryString: string) => void;
86
78
  onShowMore: () => void;
87
79
  onUpdateDataSorting: (updateSorting: WfoDataSorting<T>) => void;
80
+ // Resolves a column key to its search field path (e.g. "status" -> "subscription.status"), used
81
+ // when a column header search adds a condition to the filter query.
82
+ getColumnSearchFieldName?: (field: keyof T) => string;
88
83
  onExportData?: () => void;
89
84
  retrieverType: RetrieverType;
90
85
  onUpdateRetrieverType: (newRetrieverType: RetrieverType) => void;
@@ -93,11 +88,12 @@ export type WfoStructuredSearchTableProps<T extends object> = Omit<
93
88
  isValidFilterString?: boolean;
94
89
  queryBuilderRuleGroup?: RuleGroupType;
95
90
  onUpdateQueryBuilder: (ruleGroup: RuleGroupType | false) => void;
96
- handleSearch: () => void;
91
+ handleSearch: (searchParams?: SearchParams) => void;
97
92
  pageSize: number;
98
93
  setPageSize: React.Dispatch<React.SetStateAction<number>>;
99
94
  totalItems: number | false;
100
95
  hasNextPage: boolean;
96
+ prefilledFieldOptions: FieldToOperatorMap;
101
97
  };
102
98
 
103
99
  export const WfoStructuredSearchTable = <T extends object>({
@@ -119,6 +115,7 @@ export const WfoStructuredSearchTable = <T extends object>({
119
115
  queryBuilderRuleGroup,
120
116
  onUpdateQueryBuilder,
121
117
  onUpdateDataSorting,
118
+ getColumnSearchFieldName,
122
119
  handleSearch,
123
120
  pageSize,
124
121
  setPageSize,
@@ -128,12 +125,13 @@ export const WfoStructuredSearchTable = <T extends object>({
128
125
  hasNextPage,
129
126
  data,
130
127
  isLoading,
128
+ prefilledFieldOptions,
131
129
  ...tableProps
132
130
  }: WfoStructuredSearchTableProps<T>) => {
133
131
  const { theme } = useOrchestratorTheme();
134
- const { formFieldBaseStyle } = useWithOrchestratorTheme(getFormFieldsBaseStyle);
135
132
 
136
133
  const [hiddenColumns, setHiddenColumns] = useState<TableColumnKeys<T>>(defaultHiddenColumns);
134
+ const [isFilterBuilderVisible, setIsFilterBuilderVisible] = useState(false);
137
135
  const [showTableSettingsModal, setShowTableSettingsModal] = useState(false);
138
136
  const [rowDetailModalData, setRowDetailModalData] = useState<T | undefined>(undefined);
139
137
  const [showInformationModal, setShowInformationModal] = useState(false);
@@ -145,6 +143,12 @@ export const WfoStructuredSearchTable = <T extends object>({
145
143
  }
146
144
  }, [defaultHiddenColumns]);
147
145
 
146
+ useEffect(() => {
147
+ if (filterString) {
148
+ setIsFilterBuilderVisible(true);
149
+ }
150
+ }, [filterString]);
151
+
148
152
  const detailsIconColumn: WfoStructuredSearchTableColumnConfig<T> = {
149
153
  viewDetails: {
150
154
  columnType: ColumnType.CONTROL,
@@ -198,75 +202,48 @@ export const WfoStructuredSearchTable = <T extends object>({
198
202
  clearTableConfigFromLocalStorage(localStorageKey);
199
203
  };
200
204
 
205
+ const handleColumnFilterSearch = ({ field, searchText }: WfoDataSearch<T>) => {
206
+ const columnFilter = buildColumnFilter(field, searchText, filterString, getColumnSearchFieldName);
207
+ if (!columnFilter) {
208
+ return;
209
+ }
210
+ onUpdateFilterString(columnFilter.filterString);
211
+ handleSearch({ ruleGroup: columnFilter.ruleGroup });
212
+ };
213
+
214
+ const filterBuilder = (
215
+ <WfoFilterBuilder
216
+ filterString={filterString}
217
+ onUpdateFilterString={onUpdateFilterString}
218
+ isValidFilterString={isValidFilterString}
219
+ queryBuilderRuleGroup={queryBuilderRuleGroup}
220
+ onUpdateQueryBuilder={onUpdateQueryBuilder}
221
+ handleSearch={handleSearch}
222
+ isFilterBuilderVisible={isFilterBuilderVisible}
223
+ onToggleFilterBuilder={setIsFilterBuilderVisible}
224
+ prefilledFieldOptions={prefilledFieldOptions}
225
+ />
226
+ );
227
+
201
228
  return (
202
229
  <>
203
- <EuiFlexGroup alignItems="center">
204
- <EuiFlexItem>
205
- <WfoFilterBuilder
206
- filterString={filterString}
207
- onUpdateFilterString={onUpdateFilterString}
208
- isValidFilterString={isValidFilterString}
209
- queryBuilderRuleGroup={queryBuilderRuleGroup}
210
- onUpdateQueryBuilder={onUpdateQueryBuilder}
211
- handleSearch={handleSearch}
212
- />
213
- </EuiFlexItem>
230
+ <EuiFlexGroup alignItems="center" gutterSize="s">
231
+ {!isFilterBuilderVisible && <EuiFlexItem grow={false}>{filterBuilder}</EuiFlexItem>}
232
+ <WfoSearchFieldWithActions
233
+ queryText={queryText}
234
+ onChangeQueryText={onChangeQueryText}
235
+ onSearchQueryText={onSearchQueryText}
236
+ onShowInformation={() => setShowInformationModal(true)}
237
+ onShowTableSettings={() => setShowTableSettingsModal(true)}
238
+ />
214
239
  </EuiFlexGroup>
215
240
 
216
- <EuiFlexGroup alignItems="center">
217
- <EuiFlexItem>
218
- <EuiFormRow fullWidth>
219
- <EuiFieldSearch
220
- css={formFieldBaseStyle}
221
- value={queryText}
222
- placeholder={`${t('search')}...`}
223
- onChange={(e) => onChangeQueryText(e.target.value)}
224
- onSearch={(queryText) => onSearchQueryText(queryText)}
225
- fullWidth
226
- />
227
- </EuiFormRow>
228
- </EuiFlexItem>
229
- <EuiFlexItem grow={false}>
230
- <EuiSelect
231
- options={[
232
- {
233
- value: RetrieverType.Auto,
234
- text: t('retrieverAuto'),
235
- },
236
- {
237
- value: RetrieverType.Fuzzy,
238
- text: t('retrieverFuzzy'),
239
- },
240
- {
241
- value: RetrieverType.Semantic,
242
- text: t('retrieverSemantic'),
243
- },
244
- {
245
- value: RetrieverType.Hybrid,
246
- text: t('retrieverHybrid'),
247
- },
248
- ]}
249
- value={retrieverType}
250
- onChange={(e) => onUpdateRetrieverType(e.target.value as RetrieverType)}
251
- compressed
252
- prepend={t('retrieval')}
253
- />
254
- </EuiFlexItem>
255
- <EuiFlexItem grow={false}>
256
- <EuiButtonIcon
257
- onClick={() => setShowInformationModal(true)}
258
- iconSize={'l'}
259
- iconType={'info'}
260
- aria-label={t('searchModalTitle')}
261
- />
262
- </EuiFlexItem>
263
- <EuiButton onClick={() => setShowTableSettingsModal(true)}>{t('editColumns')}</EuiButton>
264
- {onExportData && (
265
- <EuiButton isLoading={exportDataIsLoading} onClick={() => onExportData()}>
266
- {t('export')}
267
- </EuiButton>
268
- )}
269
- </EuiFlexGroup>
241
+ {isFilterBuilderVisible && (
242
+ <>
243
+ <EuiSpacer size="s" />
244
+ {filterBuilder}
245
+ </>
246
+ )}
270
247
 
271
248
  {error && <WfoErrorWithMessage error={error} />}
272
249
 
@@ -277,6 +254,7 @@ export const WfoStructuredSearchTable = <T extends object>({
277
254
  hiddenColumns={hiddenColumns}
278
255
  rowExpandingConfiguration={rowExpandingConfiguration}
279
256
  onUpdateDataSorting={onUpdateDataSorting}
257
+ onUpdateDataSearch={handleColumnFilterSearch}
280
258
  dataSorting={dataSorting}
281
259
  data={data}
282
260
  isLoading={isLoading}
@@ -302,6 +280,31 @@ export const WfoStructuredSearchTable = <T extends object>({
302
280
  onClose={() => setShowTableSettingsModal(false)}
303
281
  onUpdateTableConfig={handleUpdateTableConfig}
304
282
  onResetToDefaults={handleResetToDefaults}
283
+ extraSettings={
284
+ <>
285
+ <EuiFormRow label={t('retrieval')} display="columnCompressed">
286
+ <EuiSelect
287
+ options={[
288
+ { value: RetrieverType.Auto, text: t('retrieverAuto') },
289
+ { value: RetrieverType.Fuzzy, text: t('retrieverFuzzy') },
290
+ { value: RetrieverType.Semantic, text: t('retrieverSemantic') },
291
+ { value: RetrieverType.Hybrid, text: t('retrieverHybrid') },
292
+ ]}
293
+ value={retrieverType}
294
+ onChange={(e) => onUpdateRetrieverType(e.target.value as RetrieverType)}
295
+ compressed
296
+ />
297
+ </EuiFormRow>
298
+ {onExportData && (
299
+ <>
300
+ <EuiSpacer size="m" />
301
+ <EuiButton isLoading={exportDataIsLoading} onClick={() => onExportData()} fullWidth>
302
+ {t('export')}
303
+ </EuiButton>
304
+ </>
305
+ )}
306
+ </>
307
+ }
305
308
  />
306
309
  )}
307
310
 
@@ -2,7 +2,7 @@ import { css } from '@emotion/react';
2
2
 
3
3
  import { WfoThemeHelpers } from '@/hooks';
4
4
 
5
- export const getWfoStructuredSearchTableStyles = ({ theme, toSecondaryColor }: WfoThemeHelpers) => {
5
+ export const getWfoStructuredSearchTableStyles = ({ theme }: WfoThemeHelpers) => {
6
6
  const queryBuilderContainerStyles = css({
7
7
  backgroundColor: theme.colors.backgroundBaseSubdued,
8
8
  padding: theme.base / 2,
@@ -13,20 +13,35 @@ export const getWfoStructuredSearchTableStyles = ({ theme, toSecondaryColor }: W
13
13
  const toggleButtonStyles = css({
14
14
  backgroundColor: 'primary',
15
15
  padding: theme.base * 0.75,
16
- marginLeft: theme.base,
17
- });
18
-
19
- const textAreaStyles = css({
20
- width: '100%',
21
- maxInlineSize: '100%',
22
- height: '42px',
23
- border: `this solid ${toSecondaryColor(theme.colors.borderBasePlain)}`,
24
16
  });
25
17
 
26
18
  const buttonGroupStyles = css({
27
- backgroundColor: theme.colors.textGhost,
28
- height: '100%',
29
- borderRadius: theme.border.radius.small,
19
+ alignSelf: 'center',
20
+ backgroundColor: 'transparent',
21
+ height: theme.base * 2,
22
+ '.euiButtonGroup__buttons': {
23
+ backgroundColor: 'transparent',
24
+ height: theme.base * 2,
25
+ minHeight: theme.base * 2,
26
+ },
27
+ 'button.euiButtonGroupButton': {
28
+ height: theme.base * 2,
29
+ minHeight: theme.base * 2,
30
+ blockSize: theme.base * 2,
31
+ borderRadius: 0,
32
+ transition: 'none',
33
+ },
34
+ 'button.euiButtonGroupButton:not(.euiButtonGroupButton-isSelected)': {
35
+ backgroundColor: theme.colors.backgroundBasePlain,
36
+ },
37
+ 'button.euiButtonGroupButton:first-of-type': {
38
+ borderTopLeftRadius: theme.base,
39
+ borderBottomLeftRadius: theme.base,
40
+ },
41
+ 'button.euiButtonGroupButton:last-of-type': {
42
+ borderTopRightRadius: theme.base,
43
+ borderBottomRightRadius: theme.base,
44
+ },
30
45
  });
31
46
 
32
47
  const ruleContainerStyles = css({
@@ -42,7 +57,7 @@ export const getWfoStructuredSearchTableStyles = ({ theme, toSecondaryColor }: W
42
57
 
43
58
  const ruleGroupContainerBase = {
44
59
  padding: theme.base / 2,
45
- marginTop: theme.base / 4,
60
+ border: `1px solid ${theme.colors.borderBaseSubdued}`,
46
61
  };
47
62
 
48
63
  const ruleGroupContainerBlueStyles = css({
@@ -74,12 +89,12 @@ export const getWfoStructuredSearchTableStyles = ({ theme, toSecondaryColor }: W
74
89
  const removeGroupActionStyles = css({
75
90
  backgroundColor: theme.colors.backgroundLightPrimary,
76
91
  width: '40px',
77
- marginTop: theme.base / 4,
78
92
  height: 'stretch',
79
93
  alignItems: 'center',
80
94
  justifyContent: 'center',
81
95
  borderBottomRightRadius: theme.border.radius.small,
82
96
  borderTopRightRadius: theme.border.radius.small,
97
+ border: `thin solid ${theme.colors.borderBasePlain}`,
83
98
  cursor: 'pointer',
84
99
  });
85
100
 
@@ -119,17 +134,36 @@ export const getWfoStructuredSearchTableStyles = ({ theme, toSecondaryColor }: W
119
134
  border: `thin dashed ${theme.colors.backgroundFilledPrimary}`,
120
135
  borderRadius: theme.border.radius.medium,
121
136
  alignItems: 'center',
122
- padding: theme.base,
137
+ paddingBlock: theme.base / 3,
138
+ paddingInline: theme.base,
123
139
  color: theme.colors.primary,
124
140
  justifyContent: 'center',
125
141
  flexDirection: 'row',
126
142
  cursor: 'pointer',
127
143
  });
128
144
 
145
+ const inlineCombinatorStyles = css({
146
+ alignSelf: 'start',
147
+ transform: `translateY(calc(-50% - ${theme.base / 8}px))`,
148
+ });
149
+
150
+ const ruleGroupBodyGridStyles = css({
151
+ display: 'grid',
152
+ gridTemplateColumns: 'auto 1fr',
153
+ columnGap: theme.base / 2,
154
+ rowGap: theme.base / 4,
155
+ alignItems: 'center',
156
+ '& > :first-child': {
157
+ gridColumn: '1 / -1',
158
+ },
159
+ '& > :first-child:not(:only-child)': {
160
+ gridColumn: '2 / -1',
161
+ },
162
+ });
163
+
129
164
  return {
130
165
  toggleButtonStyles,
131
166
  queryBuilderContainerStyles,
132
- textAreaStyles,
133
167
  buttonGroupStyles,
134
168
  ruleContainerStyles,
135
169
  ruleGroupContainerBlueStyles,
@@ -142,5 +176,7 @@ export const getWfoStructuredSearchTableStyles = ({ theme, toSecondaryColor }: W
142
176
  addRulePlusStyles,
143
177
  addRuleContainerStyles,
144
178
  addGroupStyles,
179
+ inlineCombinatorStyles,
180
+ ruleGroupBodyGridStyles,
145
181
  };
146
182
  };
@@ -0,0 +1,45 @@
1
+ import type { RuleGroupType } from 'react-querybuilder';
2
+ import { parseCEL } from 'react-querybuilder/parseCEL';
3
+
4
+ export const parseCelToRuleGroup = (celString: string): RuleGroupType | undefined => {
5
+ if (!celString) {
6
+ return undefined;
7
+ }
8
+ try {
9
+ const ruleGroup = parseCEL(celString);
10
+ return ruleGroup?.rules?.length > 0 ? ruleGroup : undefined;
11
+ } catch {
12
+ return undefined;
13
+ }
14
+ };
15
+
16
+ /**
17
+ * Builds a CEL filter that appends a single column condition (`field == "value"`) to the current
18
+ * filter and returns it together with its parsed rule group. The column name is resolved via
19
+ * getColumnSearchFieldName, falling back to the field key. Returns undefined when the input can't
20
+ * produce a valid filter (empty/quoted search text, or a filter that doesn't parse back to rules).
21
+ */
22
+ export const buildColumnFilter = <T>(
23
+ field: keyof T,
24
+ searchText: string,
25
+ currentFilter?: string,
26
+ getColumnSearchFieldName?: (field: keyof T) => string,
27
+ ): { filterString: string; ruleGroup: RuleGroupType } | undefined => {
28
+ // A double quote in the value would break the `== "..."` CEL literal and parseCEL has no escaping.
29
+ if (!searchText || searchText.includes('"')) {
30
+ return undefined;
31
+ }
32
+
33
+ const searchFieldName = getColumnSearchFieldName?.(field) ?? String(field);
34
+ const columnFilterCondition = `${searchFieldName} == "${searchText}"`;
35
+ const trimmedCurrentFilter = currentFilter?.trim();
36
+ const filterString =
37
+ trimmedCurrentFilter ? `(${trimmedCurrentFilter}) && ${columnFilterCondition}` : columnFilterCondition;
38
+
39
+ const ruleGroup = parseCelToRuleGroup(filterString);
40
+ if (!ruleGroup) {
41
+ return undefined;
42
+ }
43
+
44
+ return { filterString, ruleGroup };
45
+ };
@@ -25,6 +25,7 @@ export type TableSettingsModalProps<T> = {
25
25
  onClose: () => void;
26
26
  onUpdateTableConfig: (updatedTableConfig: TableSettingsConfig<T>) => void;
27
27
  onResetToDefaults: () => void;
28
+ extraSettings?: React.ReactNode;
28
29
  };
29
30
 
30
31
  export const TableSettingsModal = <T,>({
@@ -33,6 +34,7 @@ export const TableSettingsModal = <T,>({
33
34
  onUpdateTableConfig,
34
35
  onResetToDefaults,
35
36
  onClose,
37
+ extraSettings,
36
38
  }: TableSettingsModalProps<T>) => {
37
39
  const t = useTranslations('main');
38
40
  const { formRowStyle, selectFieldStyle } = useWithOrchestratorTheme(getWfoTableSettingsModalStyles);
@@ -97,6 +99,13 @@ export const TableSettingsModal = <T,>({
97
99
  options={options}
98
100
  />
99
101
  </EuiFormRow>
102
+
103
+ {extraSettings && (
104
+ <>
105
+ <EuiSpacer size="xs" />
106
+ {extraSettings}
107
+ </>
108
+ )}
100
109
  </EuiForm>
101
110
  </WfoSettingsModal>
102
111
  );
@@ -1,3 +1,4 @@
1
+ export * from './WfoAutoExpandableTextArea';
1
2
  export * from './WfoBadges';
2
3
  export * from './WfoBackendUnavailable';
3
4
  export * from './WfoAvailabilityCheck';
@@ -1 +1 @@
1
- export const ORCHESTRATOR_UI_LIBRARY_VERSION = '8.5.0';
1
+ export const ORCHESTRATOR_UI_LIBRARY_VERSION = '8.6.0';
@@ -36,6 +36,7 @@
36
36
  "deselect": "Deselect",
37
37
  "close": "Close",
38
38
  "editColumns": "Edit columns",
39
+ "tableSettings": "Table settings",
39
40
  "loading": "Loading",
40
41
  "newSubscription": "New subscription",
41
42
  "newTask": "New task",
@@ -36,6 +36,7 @@
36
36
  "deselect": "Deselecteer",
37
37
  "close": "Sluiten",
38
38
  "editColumns": "Wijzig kolommen",
39
+ "tableSettings": "Tabel instellingen",
39
40
  "loading": "Laden",
40
41
  "newSubscription": "Nieuwe subscription",
41
42
  "newTask": "Nieuwe task",