@orchestrator-ui/orchestrator-ui-components 8.6.0 → 8.7.1

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 (56) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/.turbo/turbo-test.log +15 -12
  4. package/CHANGELOG.md +18 -0
  5. package/dist/index.d.ts +416 -1183
  6. package/dist/index.js +3457 -3072
  7. package/dist/index.js.map +1 -1
  8. package/package.json +3 -1
  9. package/src/components/WfoPydanticForm/fields/WfoCron.spec.ts +88 -0
  10. package/src/components/WfoPydanticForm/fields/WfoCron.tsx +233 -0
  11. package/src/components/WfoPydanticForm/fields/WfoSummary.tsx +42 -7
  12. package/src/components/WfoPydanticForm/fields/index.ts +1 -0
  13. package/src/components/WfoPydanticForm/fields/styles.ts +92 -0
  14. package/src/components/WfoSearchPage/WfoFilterGroup/WfoFilterGroup.tsx +1 -1
  15. package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchResultItem.tsx +2 -1
  16. package/src/components/WfoSearchPage/utils.ts +37 -1
  17. package/src/components/WfoSubscription/WfoInUseByRelations.tsx +6 -2
  18. package/src/components/WfoSubscription/WfoSubscriptionActions/WfoSubscriptionActionExpandableMenuItem.tsx +19 -14
  19. package/src/components/WfoSubscription/WfoSubscriptionActions/WfoSubscriptionActions.tsx +13 -1
  20. package/src/components/WfoSubscription/WfoSubscriptionActions/WfoSubscriptionActionsMenuItem.tsx +3 -0
  21. package/src/components/WfoSubscription/WfoSubscriptionProductBlock/WfoSubscriptionProductBlock.tsx +1 -1
  22. package/src/components/WfoSubscriptionsList/WfoSubscriptionsList.tsx +5 -0
  23. package/src/components/WfoSubscriptionsList/subscriptionResultMappers.ts +3 -1
  24. package/src/components/WfoTable/WfoAdvancedTable/WfoAdvancedTable.tsx +2 -12
  25. package/src/components/WfoTable/WfoStructuredSearchTable/WfoExpandingSearchRow.tsx +21 -14
  26. package/src/components/WfoTable/WfoStructuredSearchTable/WfoFieldSelector.tsx +27 -1
  27. package/src/components/WfoTable/WfoStructuredSearchTable/WfoFilterBuilder.tsx +97 -91
  28. package/src/components/WfoTable/WfoStructuredSearchTable/WfoOperatorSelector.tsx +32 -7
  29. package/src/components/WfoTable/WfoStructuredSearchTable/WfoRuleGroup.tsx +5 -4
  30. package/src/components/WfoTable/WfoStructuredSearchTable/WfoStructuredSearchTable.tsx +32 -30
  31. package/src/components/WfoTable/WfoStructuredSearchTable/WfoValueEditor.tsx +41 -11
  32. package/src/components/WfoTable/WfoStructuredSearchTable/styles.ts +22 -5
  33. package/src/components/WfoTable/WfoTable/WfoTable.tsx +1 -0
  34. package/src/components/WfoTable/WfoTable/WfoTableDataRows.tsx +16 -4
  35. package/src/components/WfoTable/WfoTable/styles.ts +8 -0
  36. package/src/components/WfoTable/WfoTable/utils.spec.ts +62 -1
  37. package/src/components/WfoTable/WfoTable/utils.ts +4 -1
  38. package/src/components/WfoTable/WfoTableSettingsModal/WfoTableSettingsModal.spec.ts +45 -0
  39. package/src/components/WfoTable/WfoTableSettingsModal/index.ts +1 -0
  40. package/src/components/WfoTable/WfoTableSettingsModal/utils.ts +17 -0
  41. package/src/configuration/version.ts +1 -1
  42. package/src/hooks/index.ts +1 -0
  43. package/src/hooks/useGetPydanticFormsConfig.tsx +12 -0
  44. package/src/hooks/useLanguageCode.ts +11 -0
  45. package/src/messages/en-GB.json +442 -549
  46. package/src/messages/getTranslationMessages.spec.ts +47 -30
  47. package/src/messages/nl-NL.json +450 -411
  48. package/src/pages/WfoSearchPocPage.tsx +14 -5
  49. package/src/rtk/endpoints/subscriptionList.ts +1 -0
  50. package/src/theme/baseStyles/formFieldsBaseStyle.ts +0 -1
  51. package/src/types/search.ts +1 -1
  52. package/src/types/types.ts +1 -1
  53. package/src/utils/getDefaultTableConfig.ts +6 -1
  54. package/src/utils/index.ts +1 -0
  55. package/src/utils/integer.spec.ts +20 -0
  56. package/src/utils/integer.ts +3 -0
@@ -13,7 +13,8 @@ import { WfoRemoveGroupAction } from './WfoRemoveGroupAction';
13
13
 
14
14
  export const WfoRuleGroup = (props: RuleGroupProps) => {
15
15
  const ruleGroupProps = useRuleGroup(props);
16
- const ruleId = ruleGroupProps.ruleGroup.id;
16
+ const rulePath = ruleGroupProps.path;
17
+ const isRootGroup: boolean = !rulePath || rulePath.length === 0;
17
18
 
18
19
  const {
19
20
  ruleGroupContainerBlueStyles,
@@ -24,9 +25,9 @@ export const WfoRuleGroup = (props: RuleGroupProps) => {
24
25
  } = useWithOrchestratorTheme(getWfoStructuredSearchTableStyles);
25
26
  const getGroupContainerStyles = () => {
26
27
  if (ruleGroupProps.path.length % 2) {
27
- return ruleId !== 'root' ? innerGroupContainerWhiteStyles : ruleGroupContainerWhiteStyles;
28
+ return !isRootGroup ? innerGroupContainerWhiteStyles : ruleGroupContainerWhiteStyles;
28
29
  } else {
29
- return ruleId !== 'root' ? innerGroupContainerBlueStyles : ruleGroupContainerBlueStyles;
30
+ return !isRootGroup ? innerGroupContainerBlueStyles : ruleGroupContainerBlueStyles;
30
31
  }
31
32
  };
32
33
 
@@ -70,7 +71,7 @@ export const WfoRuleGroup = (props: RuleGroupProps) => {
70
71
  </EuiFlexItem>
71
72
  </EuiFlexGroup>
72
73
  </EuiFlexItem>
73
- {ruleId !== 'root' && <WfoRemoveGroupAction onClick={ruleGroupProps.removeGroup} />}
74
+ {!isRootGroup && <WfoRemoveGroupAction onClick={ruleGroupProps.removeGroup} />}
74
75
  </EuiFlexGroup>
75
76
  );
76
77
  };
@@ -9,7 +9,6 @@ import {
9
9
  DEFAULT_PAGE_SIZE,
10
10
  DEFAULT_PAGE_SIZES,
11
11
  TableColumnKeys,
12
- TableSettingsColumnConfig,
13
12
  TableSettingsConfig,
14
13
  TableSettingsModal,
15
14
  WfoDataSearch,
@@ -19,6 +18,7 @@ import {
19
18
  WfoKeyValueTable,
20
19
  WfoKeyValueTableDataType,
21
20
  clearTableConfigFromLocalStorage,
21
+ getTableSettingsColumns,
22
22
  setTableConfigToLocalStorage,
23
23
  } from '@/components';
24
24
  import { getRowDetailData } from '@/components/WfoTable/WfoAdvancedTable/getRowDetailData';
@@ -27,7 +27,7 @@ import {
27
27
  WfoTableControlColumnConfigItem,
28
28
  WfoTableDataColumnConfigItem,
29
29
  } from '@/components/WfoTable/WfoTable';
30
- import { useOrchestratorTheme } from '@/hooks';
30
+ import { useOrchestratorTheme, useWithOrchestratorTheme } from '@/hooks';
31
31
  import { WfoArrowsExpand } from '@/icons';
32
32
  import { WfoGraphqlError } from '@/rtk';
33
33
  import { FieldToOperatorMap, RetrieverType } from '@/types';
@@ -36,6 +36,7 @@ import { getDefaultTableConfig } from '@/utils';
36
36
  import { ColumnType, WfoTable, WfoTableProps } from '../WfoTable';
37
37
  import { WfoFilterBuilder } from './WfoFilterBuilder';
38
38
  import { WfoSearchFieldWithActions } from './WfoSearchFieldWithActions';
39
+ import { getWfoStructuredSearchTableStyles } from './styles';
39
40
  import { buildColumnFilter } from './utils';
40
41
 
41
42
  export type WfoStructuredSearchTableDataColumnConfigItem<
@@ -129,7 +130,7 @@ export const WfoStructuredSearchTable = <T extends object>({
129
130
  ...tableProps
130
131
  }: WfoStructuredSearchTableProps<T>) => {
131
132
  const { theme } = useOrchestratorTheme();
132
-
133
+ const { toggleButtonStyles } = useWithOrchestratorTheme(getWfoStructuredSearchTableStyles);
133
134
  const [hiddenColumns, setHiddenColumns] = useState<TableColumnKeys<T>>(defaultHiddenColumns);
134
135
  const [isFilterBuilderVisible, setIsFilterBuilderVisible] = useState(false);
135
136
  const [showTableSettingsModal, setShowTableSettingsModal] = useState(false);
@@ -166,17 +167,7 @@ export const WfoStructuredSearchTable = <T extends object>({
166
167
  ...tableColumnConfig,
167
168
  };
168
169
 
169
- const tableSettingsColumns: TableSettingsColumnConfig<T>[] = Object.entries(tableColumnConfig).map(
170
- ([key, { label }]): TableSettingsColumnConfig<T> => {
171
- const field = key as keyof T;
172
-
173
- return {
174
- field,
175
- name: label,
176
- isVisible: hiddenColumns.indexOf(field) === -1,
177
- };
178
- },
179
- );
170
+ const tableSettingsColumns = getTableSettingsColumns(tableColumnConfig, hiddenColumns);
180
171
 
181
172
  const rowDetailData: WfoKeyValueTableDataType[] | undefined =
182
173
  rowDetailModalData && getRowDetailData(rowDetailModalData, tableColumnConfig);
@@ -211,24 +202,26 @@ export const WfoStructuredSearchTable = <T extends object>({
211
202
  handleSearch({ ruleGroup: columnFilter.ruleGroup });
212
203
  };
213
204
 
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
-
228
205
  return (
229
206
  <>
230
207
  <EuiFlexGroup alignItems="center" gutterSize="s">
231
- {!isFilterBuilderVisible && <EuiFlexItem grow={false}>{filterBuilder}</EuiFlexItem>}
208
+ {!isFilterBuilderVisible && (
209
+ <EuiFlexItem grow={false}>
210
+ <EuiButton
211
+ css={toggleButtonStyles}
212
+ onClick={() => setIsFilterBuilderVisible(true)}
213
+ id={'button-toggle-filter-builder'}
214
+ data-test-id={'button-toggle-filter-builder'}
215
+ fill
216
+ type="submit"
217
+ iconType="filter"
218
+ iconSide="left"
219
+ aria-label={t('createFilter')}
220
+ >
221
+ {t('createFilter')}
222
+ </EuiButton>
223
+ </EuiFlexItem>
224
+ )}
232
225
  <WfoSearchFieldWithActions
233
226
  queryText={queryText}
234
227
  onChangeQueryText={onChangeQueryText}
@@ -241,7 +234,16 @@ export const WfoStructuredSearchTable = <T extends object>({
241
234
  {isFilterBuilderVisible && (
242
235
  <>
243
236
  <EuiSpacer size="s" />
244
- {filterBuilder}
237
+ <WfoFilterBuilder
238
+ filterString={filterString}
239
+ onUpdateFilterString={onUpdateFilterString}
240
+ isValidFilterString={isValidFilterString}
241
+ queryBuilderRuleGroup={queryBuilderRuleGroup}
242
+ onUpdateQueryBuilder={onUpdateQueryBuilder}
243
+ handleSearch={handleSearch}
244
+ onToggleFilterBuilder={setIsFilterBuilderVisible}
245
+ prefilledFieldOptions={prefilledFieldOptions}
246
+ />
245
247
  </>
246
248
  )}
247
249
 
@@ -137,23 +137,53 @@ const DatePicker = ({ handleOnChange, rangeIndex, value: currentValue }: WfoRang
137
137
  );
138
138
  };
139
139
 
140
- export const WfoValueEditor = ({ field: fieldName, context, handleOnChange, operator, value }: ValueEditorProps) => {
140
+ export const WfoValueEditor = ({
141
+ field: fieldName,
142
+ context,
143
+ handleOnChange,
144
+ operator,
145
+ value,
146
+ className,
147
+ }: ValueEditorProps) => {
141
148
  const fieldPathInfoMap = context?.fieldPathInfoMap;
142
149
 
143
150
  const fieldInfo = fieldPathInfoMap && fieldPathInfoMap.has(fieldName) ? fieldPathInfoMap.get(fieldName) : undefined;
144
151
  const uiFieldType = fieldInfo?.ui_types[0] || UiFieldType.text;
145
152
 
146
- if (uiFieldType === UiFieldType.boolean) {
147
- return <BooleanEditor handleOnChange={handleOnChange} value={value} />;
148
- }
153
+ const getEditor = () => {
154
+ if (uiFieldType === UiFieldType.boolean) {
155
+ return <BooleanEditor handleOnChange={handleOnChange} value={value} />;
156
+ }
149
157
 
150
- if (uiFieldType === UiFieldType.datetime) {
151
- return <WfoRangeEditor handleOnChange={handleOnChange} operator={operator} Element={DatePicker} value={value} />;
152
- }
158
+ if (uiFieldType === UiFieldType.datetime) {
159
+ return <WfoRangeEditor handleOnChange={handleOnChange} operator={operator} Element={DatePicker} value={value} />;
160
+ }
153
161
 
154
- if (uiFieldType === UiFieldType.number) {
155
- return <WfoRangeEditor handleOnChange={handleOnChange} operator={operator} Element={NumberEditor} value={value} />;
156
- }
162
+ if (uiFieldType === UiFieldType.number) {
163
+ return (
164
+ <WfoRangeEditor handleOnChange={handleOnChange} operator={operator} Element={NumberEditor} value={value} />
165
+ );
166
+ }
167
+
168
+ return <TextEditor handleOnChange={handleOnChange} value={value} />;
169
+ };
157
170
 
158
- return <TextEditor handleOnChange={handleOnChange} value={value} />;
171
+ const handleWrapperKeyDown: KeyboardEventHandler<HTMLDivElement> = (event) => {
172
+ if (event.key !== 'Enter') return;
173
+ // Restrict to the editor inputs: the editors' own Enter handlers have already run
174
+ // (bubble phase) and committed the value via blur, so the search sees it. Enter on
175
+ // the boolean buttons means "select" — its click fires only after this keydown, so
176
+ // searching there would use the pre-toggle value.
177
+ if (!(event.target instanceof HTMLInputElement)) return;
178
+ context?.onValueEditorEnter?.();
179
+ };
180
+
181
+ // react-querybuilder delivers the standard `rule-value` class via this prop; the wrapper
182
+ // makes it queryable in the DOM (WfoFieldSelector relies on it to move focus here).
183
+ // `display: contents` keeps the children direct participants in the rule's flex row.
184
+ return (
185
+ <div className={className} style={{ display: 'contents' }} onKeyDown={handleWrapperKeyDown}>
186
+ {getEditor()}
187
+ </div>
188
+ );
159
189
  };
@@ -107,11 +107,12 @@ export const getWfoStructuredSearchTableStyles = ({ theme }: WfoThemeHelpers) =>
107
107
  });
108
108
  const expandingRowBodyStyles = css({
109
109
  display: 'flex',
110
- gap: theme.base,
111
- fontWeight: theme.font.weight.bold,
112
- span: {
113
- fontWeight: 'bold',
114
- },
110
+ justifyContent: 'space-between',
111
+ // Size to the visible scroll viewport (minus the td padding) instead of
112
+ // the full table width, and keep it pinned there on horizontal scroll
113
+ position: 'sticky',
114
+ left: theme.base / 2,
115
+ width: `calc(100cqw - ${theme.base}px)`,
115
116
  });
116
117
 
117
118
  const addRulePlusStyles = css({
@@ -161,6 +162,19 @@ export const getWfoStructuredSearchTableStyles = ({ theme }: WfoThemeHelpers) =>
161
162
  },
162
163
  });
163
164
 
165
+ const hideExpandedRowStyle = css({
166
+ display: 'none',
167
+ });
168
+ const dotStyles = css({
169
+ padding: theme.base / 4,
170
+ });
171
+
172
+ const expandingRowFieldStyles = css({
173
+ display: 'flex',
174
+ padding: theme.base / 4,
175
+ alignItems: 'center',
176
+ });
177
+
164
178
  return {
165
179
  toggleButtonStyles,
166
180
  queryBuilderContainerStyles,
@@ -178,5 +192,8 @@ export const getWfoStructuredSearchTableStyles = ({ theme }: WfoThemeHelpers) =>
178
192
  addGroupStyles,
179
193
  inlineCombinatorStyles,
180
194
  ruleGroupBodyGridStyles,
195
+ hideExpandedRowStyle,
196
+ expandingRowFieldStyles,
197
+ dotStyles,
181
198
  };
182
199
  };
@@ -82,6 +82,7 @@ export type WfoTableProps<T extends object> = {
82
82
  rowExpandingConfiguration?: {
83
83
  uniqueRowId: keyof WfoTableColumnConfig<T>;
84
84
  uniqueRowIdToExpandedRowMap: Record<string, ReactNode>;
85
+ shouldOnlyShowOnHover?: boolean;
85
86
  };
86
87
  pagination?: Pagination;
87
88
  overrideHeader?: (
@@ -27,8 +27,15 @@ export const WfoTableDataRows = <T extends object>({
27
27
  onRowClick,
28
28
  className,
29
29
  }: WfoTableDataRowsProps<T>) => {
30
- const { cellStyle, cellContentStyle, rowStyle, dataRowStyle, clickableStyle, setWidth } =
31
- useWithOrchestratorTheme(getWfoTableStyles);
30
+ const {
31
+ cellStyle,
32
+ cellContentStyle,
33
+ rowStyle,
34
+ dataRowStyle,
35
+ clickableStyle,
36
+ setWidth,
37
+ toggleExpandedRowOnHoverStyle,
38
+ } = useWithOrchestratorTheme(getWfoTableStyles);
32
39
 
33
40
  const sortedVisibleColumns = getSortedVisibleColumns(columnConfig, columnOrder, hiddenColumns);
34
41
 
@@ -37,8 +44,13 @@ export const WfoTableDataRows = <T extends object>({
37
44
  {data.map((row, index) => (
38
45
  <Fragment key={`table-data-row-${index}`}>
39
46
  <tr
40
- className={`${className} ${DATA_ROW_CLASS}`}
41
- css={[rowStyle, dataRowStyle, onRowClick && clickableStyle]}
47
+ className={className ? `${className} ${DATA_ROW_CLASS}` : DATA_ROW_CLASS}
48
+ css={[
49
+ rowStyle,
50
+ dataRowStyle,
51
+ onRowClick && clickableStyle,
52
+ rowExpandingConfiguration?.shouldOnlyShowOnHover && toggleExpandedRowOnHoverStyle,
53
+ ]}
42
54
  onClick={() => onRowClick?.(row)}
43
55
  >
44
56
  {sortedVisibleColumns.map(([key, columnConfig]) => {
@@ -31,6 +31,9 @@ export const getWfoTableStyles = ({ theme, isDarkModeActive }: WfoThemeHelpers)
31
31
 
32
32
  const tableContainerStyle = css({
33
33
  overflowX: 'auto',
34
+ // Query container so descendants (e.g. expanded rows) can size to the
35
+ // visible table width with cqw units instead of the full table width
36
+ containerType: 'inline-size',
34
37
  });
35
38
 
36
39
  const tableStyle = css({
@@ -157,6 +160,10 @@ export const getWfoTableStyles = ({ theme, isDarkModeActive }: WfoThemeHelpers)
157
160
  '.eui-xScroll': { display: 'flex', justifyContent: 'flex-start' },
158
161
  });
159
162
 
163
+ const toggleExpandedRowOnHoverStyle = css({
164
+ '&:hover + tr, &:focus-within + tr': { display: 'table-row' },
165
+ });
166
+
160
167
  return {
161
168
  tableContainerStyle,
162
169
  tableStyle,
@@ -174,5 +181,6 @@ export const getWfoTableStyles = ({ theme, isDarkModeActive }: WfoThemeHelpers)
174
181
  dragAndDropStyle,
175
182
  paginationStyle,
176
183
  setWidth,
184
+ toggleExpandedRowOnHoverStyle,
177
185
  };
178
186
  };
@@ -1,7 +1,12 @@
1
+ import { TableColumnKeys } from '@/components';
1
2
  import { ColumnType, WfoTableColumnConfig } from '@/components/WfoTable/WfoTable/WfoTable';
2
3
  import { SortOrder } from '@/types';
3
4
 
4
- import { getUpdatedSortOrder, mapSortableAndFilterableValuesToTableColumnConfig } from './utils';
5
+ import {
6
+ getSortedVisibleColumns,
7
+ getUpdatedSortOrder,
8
+ mapSortableAndFilterableValuesToTableColumnConfig,
9
+ } from './utils';
5
10
 
6
11
  type TestObject = {
7
12
  name: string;
@@ -19,6 +24,21 @@ const tableColumnConfig: WfoTableColumnConfig<TestObject> = {
19
24
  },
20
25
  };
21
26
 
27
+ const tableColumnConfigWithControlColumn: WfoTableColumnConfig<TestObject> = {
28
+ actions: {
29
+ columnType: ColumnType.CONTROL,
30
+ renderControl: () => null,
31
+ },
32
+ name: {
33
+ columnType: ColumnType.DATA,
34
+ label: 'testName',
35
+ },
36
+ age: {
37
+ columnType: ColumnType.DATA,
38
+ label: 'testAge',
39
+ },
40
+ };
41
+
22
42
  describe('utils', () => {
23
43
  describe('getUpdatedSortOrder()', () => {
24
44
  it('returns SortOrder.DESC if the currentSortOrder is SortOrder.ASC', () => {
@@ -38,6 +58,47 @@ describe('utils', () => {
38
58
  });
39
59
  });
40
60
 
61
+ describe('getSortedVisibleColumns()', () => {
62
+ it('keeps a control column visible even when its key is present in hiddenColumns', () => {
63
+ // Given a stale hiddenColumns still listing the control column (e.g. from localStorage)
64
+ const hiddenColumns = ['actions'] as unknown as TableColumnKeys<TestObject>;
65
+
66
+ // When
67
+ const result = getSortedVisibleColumns(tableColumnConfigWithControlColumn, [], hiddenColumns);
68
+
69
+ // Then the control column is still rendered
70
+ const visibleKeys = result.map(([key]) => key);
71
+ expect(visibleKeys).toContain('actions');
72
+ });
73
+
74
+ it('hides data columns that are listed in hiddenColumns', () => {
75
+ // Given
76
+ const hiddenColumns: TableColumnKeys<TestObject> = ['age'];
77
+
78
+ // When
79
+ const result = getSortedVisibleColumns(tableColumnConfigWithControlColumn, [], hiddenColumns);
80
+
81
+ // Then
82
+ const visibleKeys = result.map(([key]) => key);
83
+ expect(visibleKeys).not.toContain('age');
84
+ expect(visibleKeys).toContain('name');
85
+ });
86
+
87
+ it('keeps the control column visible while still hiding a hidden data column', () => {
88
+ // Given both a control key and a data key are marked hidden
89
+ const hiddenColumns = ['actions', 'age'] as unknown as TableColumnKeys<TestObject>;
90
+
91
+ // When
92
+ const result = getSortedVisibleColumns(tableColumnConfigWithControlColumn, [], hiddenColumns);
93
+
94
+ // Then only the data column is hidden; the control column stays visible
95
+ const visibleKeys = result.map(([key]) => key);
96
+ expect(visibleKeys).toContain('actions');
97
+ expect(visibleKeys).toContain('name');
98
+ expect(visibleKeys).not.toContain('age');
99
+ });
100
+ });
101
+
41
102
  describe('mapSortableAndFilterableValuesToTableColumnConfig()', () => {
42
103
  it('sets the sortable and filterable properties for the columnConfig object to true when the colum name is specified in the list', () => {
43
104
  // Given
@@ -25,7 +25,10 @@ export const getSortedVisibleColumns = <T extends object>(
25
25
  ),
26
26
  );
27
27
 
28
- return tableHeadersEntries.filter(([columnId]) => !hiddenColumns.includes(columnId as keyof T));
28
+ return tableHeadersEntries.filter(
29
+ ([columnId, columnConfig]) =>
30
+ columnConfig.columnType === ColumnType.CONTROL || !hiddenColumns.includes(columnId as keyof T),
31
+ );
29
32
  };
30
33
 
31
34
  export const getUpdatedSortOrder = (currentSortOrder?: SortOrder) =>
@@ -0,0 +1,45 @@
1
+ import { TableColumnKeys } from '@/components';
2
+ import { ColumnType, WfoTableColumnConfig } from '@/components/WfoTable/WfoTable/WfoTable';
3
+ import { getTableSettingsColumns } from '@/components/WfoTable/WfoTableSettingsModal/utils';
4
+
5
+ type TestObject = {
6
+ name: string;
7
+ age: number;
8
+ };
9
+
10
+ const columnConfig: WfoTableColumnConfig<TestObject> = {
11
+ actions: {
12
+ columnType: ColumnType.CONTROL,
13
+ renderControl: () => null,
14
+ },
15
+ name: {
16
+ columnType: ColumnType.DATA,
17
+ label: 'Name',
18
+ },
19
+ age: {
20
+ columnType: ColumnType.DATA,
21
+ label: 'Age',
22
+ },
23
+ };
24
+
25
+ describe('getTableSettingsColumns()', () => {
26
+ it('excludes control columns and keeps only data columns as toggles', () => {
27
+ const result = getTableSettingsColumns(columnConfig, []);
28
+ expect(result.map((column) => column.field)).toEqual(['name', 'age']);
29
+ });
30
+
31
+ it('maps each data column label to name and marks all visible when nothing is hidden', () => {
32
+ const result = getTableSettingsColumns(columnConfig, []);
33
+ expect(result).toEqual([
34
+ { field: 'name', name: 'Name', isVisible: true },
35
+ { field: 'age', name: 'Age', isVisible: true },
36
+ ]);
37
+ });
38
+
39
+ it('marks a data column as not visible when it is present in hiddenColumns', () => {
40
+ const hiddenColumns: TableColumnKeys<TestObject> = ['age'];
41
+ const result = getTableSettingsColumns(columnConfig, hiddenColumns);
42
+ expect(result.find((column) => column.field === 'age')?.isVisible).toBe(false);
43
+ expect(result.find((column) => column.field === 'name')?.isVisible).toBe(true);
44
+ });
45
+ });
@@ -1 +1,2 @@
1
1
  export * from './WfoTableSettingsModal';
2
+ export * from './utils';
@@ -0,0 +1,17 @@
1
+ import { ColumnType, TableColumnKeys, TableSettingsColumnConfig, WfoTableColumnConfig } from '@/components';
2
+
3
+ export const getTableSettingsColumns = <T extends object>(
4
+ columnConfig: WfoTableColumnConfig<T>,
5
+ hiddenColumns: TableColumnKeys<T>,
6
+ ): TableSettingsColumnConfig<T>[] =>
7
+ Object.entries(columnConfig)
8
+ .filter(([, columnItemConfig]) => columnItemConfig.columnType === ColumnType.DATA)
9
+ .map(([key, { label }]): TableSettingsColumnConfig<T> => {
10
+ const field = key as keyof T;
11
+
12
+ return {
13
+ field,
14
+ name: label,
15
+ isVisible: hiddenColumns.indexOf(field) === -1,
16
+ };
17
+ });
@@ -1 +1 @@
1
- export const ORCHESTRATOR_UI_LIBRARY_VERSION = '8.6.0';
1
+ export const ORCHESTRATOR_UI_LIBRARY_VERSION = '8.7.1';
@@ -15,3 +15,4 @@ export * from './useGetSchedulesForWorkflow';
15
15
  export * from './useGetWorkflowNameById';
16
16
  export * from './usePathAutoComplete';
17
17
  export * from './useGetPydanticFormsConfig';
18
+ export * from './useLanguageCode';
@@ -23,6 +23,7 @@ import {
23
23
  WfoArrayField,
24
24
  WfoCallout,
25
25
  WfoCheckbox,
26
+ WfoCron,
26
27
  WfoDivider,
27
28
  WfoDropdown,
28
29
  WfoInteger,
@@ -181,6 +182,17 @@ const useGetComponentMatcherExtender = (): ComponentMatcherExtender => {
181
182
  return type === PydanticFormFieldType.STRING && format === ('callout' as PydanticFormFieldFormat);
182
183
  },
183
184
  },
185
+ {
186
+ id: 'cron',
187
+ ElementMatch: {
188
+ isControlledElement: true,
189
+ Element: WfoCron,
190
+ },
191
+ matcher: (field) => {
192
+ const { type, schema } = field;
193
+ return type === PydanticFormFieldType.STRING && schema.title === 'Cron';
194
+ },
195
+ },
184
196
  {
185
197
  id: 'markdownField',
186
198
  ElementMatch: {
@@ -0,0 +1,11 @@
1
+ import { useLocale } from 'next-intl';
2
+
3
+ export const useLanguageCode = () => {
4
+ const locale = useLocale();
5
+ if (!locale) return 'en';
6
+ try {
7
+ return typeof Intl !== 'undefined' && 'Locale' in Intl ? new Intl.Locale(locale).language : locale.split(/[-_]/)[0];
8
+ } catch {
9
+ return locale.split(/[-_]/)[0] || 'en';
10
+ }
11
+ };