@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchestrator-ui/orchestrator-ui-components",
3
- "version": "8.5.0",
3
+ "version": "8.6.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Library of UI Components used to display the workflow orchestrator frontend",
6
6
  "author": {
@@ -0,0 +1,54 @@
1
+ import React, { useCallback, useEffect, useRef } from 'react';
2
+
3
+ import { EuiTextArea, EuiTextAreaProps } from '@elastic/eui';
4
+
5
+ import { useWithOrchestratorTheme } from '@/hooks';
6
+
7
+ import { getStyles } from './styles';
8
+
9
+ export const WfoAutoExpandableTextArea = ({ inputRef, onChange, ...restProps }: EuiTextAreaProps) => {
10
+ const { autoExpandableTextAreaStyles } = useWithOrchestratorTheme(getStyles);
11
+ const textAreaRef = useRef<HTMLTextAreaElement | null>(null);
12
+
13
+ const adjustTextAreaHeight = (textArea: HTMLTextAreaElement | null) => {
14
+ if (!textArea) {
15
+ return;
16
+ }
17
+ textArea.style.height = 'auto';
18
+ textArea.style.height = `${textArea.scrollHeight}px`;
19
+ };
20
+
21
+ useEffect(() => {
22
+ adjustTextAreaHeight(textAreaRef.current);
23
+ }, [restProps.value]);
24
+
25
+ const handleInputRef = useCallback(
26
+ (node: HTMLTextAreaElement | null) => {
27
+ textAreaRef.current = node;
28
+ adjustTextAreaHeight(node);
29
+
30
+ if (typeof inputRef === 'function') {
31
+ inputRef(node);
32
+ } else if (inputRef) {
33
+ (inputRef as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;
34
+ }
35
+ },
36
+ [inputRef],
37
+ );
38
+
39
+ return (
40
+ <EuiTextArea
41
+ css={autoExpandableTextAreaStyles}
42
+ rows={1}
43
+ fullWidth={true}
44
+ isClearable={true}
45
+ resize={'none'}
46
+ {...restProps}
47
+ inputRef={handleInputRef}
48
+ onChange={(event) => {
49
+ adjustTextAreaHeight(event.target);
50
+ onChange?.(event);
51
+ }}
52
+ />
53
+ );
54
+ };
@@ -0,0 +1 @@
1
+ export { WfoAutoExpandableTextArea } from './WfoAutoExpandableTextArea';
@@ -0,0 +1,18 @@
1
+ import { css } from '@emotion/react';
2
+
3
+ import { WfoThemeHelpers } from '@/hooks';
4
+
5
+ export const getStyles = ({ theme, toSecondaryColor }: WfoThemeHelpers) => {
6
+ const autoExpandableTextAreaStyles = css({
7
+ width: '100%',
8
+ maxInlineSize: '100%',
9
+ height: 'auto',
10
+ overflowY: 'hidden',
11
+ resize: 'none',
12
+ border: `thin solid ${toSecondaryColor(theme.colors.borderBasePlain)}`,
13
+ });
14
+
15
+ return {
16
+ autoExpandableTextAreaStyles,
17
+ };
18
+ };
@@ -3,6 +3,7 @@ export const PATH_START_NEW_WORKFLOW = '/workflows/new';
3
3
  export const PATH_START_NEW_TASK = '/tasks/new';
4
4
  export const PATH_WORKFLOWS = '/workflows';
5
5
  export const PATH_SUBSCRIPTIONS = '/subscriptions';
6
+ export const PATH_SUBSCRIPTIONS_BETA = '/beta-subscriptions';
6
7
  export const PATH_METADATA = '/metadata';
7
8
  export const PATH_METADATA_PRODUCTS = '/metadata/products';
8
9
  export const PATH_METADATA_PRODUCT_BLOCKS = '/metadata/productblocks';
@@ -12,7 +12,7 @@ export const WfoAddGroupAction = ({ disabled, handleOnClick }: ActionProps) => {
12
12
  const t = useTranslations('search.page');
13
13
  const { addGroupStyles, addRulePlusStyles } = useWithOrchestratorTheme(getWfoStructuredSearchTableStyles);
14
14
  return disabled ? null : (
15
- <EuiFlexItem css={addGroupStyles} onClick={() => handleOnClick()}>
15
+ <EuiFlexItem grow={true} css={addGroupStyles} onClick={() => handleOnClick()}>
16
16
  <span css={addRulePlusStyles}>+</span>&nbsp;{t('addGroup')}
17
17
  </EuiFlexItem>
18
18
  );
@@ -8,6 +8,9 @@ import { useWithOrchestratorTheme } from '@/hooks';
8
8
 
9
9
  export const WfoCombinatorSelector = (props: CombinatorSelectorProps) => {
10
10
  const { buttonGroupStyles } = useWithOrchestratorTheme(getWfoStructuredSearchTableStyles);
11
+
12
+ if (props.rules.length < 2) return null;
13
+
11
14
  const options = props.options.map((option) => ({
12
15
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
13
16
  // @ts-ignore This seems to be a typing error in the react-querybuilde library. Option.name does exist
@@ -1,4 +1,4 @@
1
- import React, { useState } from 'react';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
2
  import { FieldSelectorProps } from 'react-querybuilder';
3
3
 
4
4
  import { useTranslations } from 'next-intl';
@@ -7,16 +7,21 @@ import type { EuiComboBoxOptionOption } from '@elastic/eui';
7
7
  import { EuiComboBox } from '@elastic/eui';
8
8
 
9
9
  import { usePathAutocomplete } from '@/hooks';
10
- import { EntityKind, PathInfo } from '@/types';
10
+ import { EntityKind, FieldToOperatorMap, PathInfo } from '@/types';
11
11
 
12
12
  export const WfoFieldSelector = ({ handleOnChange, disabled, rule, context }: FieldSelectorProps) => {
13
13
  const { field } = rule;
14
+ const prefilledFieldOptions: FieldToOperatorMap = context.prefilledFieldOptions;
14
15
  const [selectedValue, setSelectedValue] = useState<string>(field);
16
+ const [searchInput, setSearchInput] = useState<HTMLInputElement | null>(null);
17
+ const optionsRef = useRef<EuiComboBoxOptionOption<string>[]>([]);
18
+ const handleFieldSelectionRef = useRef<(selected: EuiComboBoxOptionOption<string>[]) => void>(() => {});
15
19
  const t = useTranslations('search.page');
16
20
  const getOption = (path: string) => ({
17
21
  value: path,
18
22
  label: path,
19
23
  });
24
+
20
25
  const getOptionsFromPathInfo = (pathInfos: PathInfo[]): EuiComboBoxOptionOption<string>[] => {
21
26
  const pathOptions: EuiComboBoxOptionOption<string>[] = [];
22
27
 
@@ -40,14 +45,26 @@ export const WfoFieldSelector = ({ handleOnChange, disabled, rule, context }: Fi
40
45
  error: errorMessage,
41
46
  } = usePathAutocomplete(selectedValue, EntityKind.SUBSCRIPTION);
42
47
 
43
- const options = getOptionsFromPathInfo(paths);
48
+ const prefilledOptions: EuiComboBoxOptionOption<string>[] = Array.from(prefilledFieldOptions.keys()).map(getOption);
49
+ const autocompleteOptions = getOptionsFromPathInfo(paths);
50
+ const placeholderOption: EuiComboBoxOptionOption<string> = {
51
+ label: '──────',
52
+ disabled: true,
53
+ };
54
+ const showPlaceholder = prefilledOptions.length > 0 && autocompleteOptions.length > 0;
55
+ const options: EuiComboBoxOptionOption<string>[] = [
56
+ ...prefilledOptions,
57
+ ...(showPlaceholder ? [placeholderOption] : []),
58
+ ...autocompleteOptions,
59
+ ];
44
60
 
45
61
  const storeFieldOperators = (selectedValue: string) => {
46
62
  const matchingPath =
47
63
  paths.find((path) => path.path === selectedValue)
48
64
  ?? paths.find((path) => path.availablePaths?.includes(selectedValue));
65
+ const operators = matchingPath?.operators ?? prefilledFieldOptions.get(selectedValue) ?? [];
49
66
 
50
- context?.onFieldSelected?.(selectedValue, matchingPath);
67
+ context?.onFieldSelected?.(selectedValue, operators);
51
68
  };
52
69
 
53
70
  const handleFieldSelection = (selectedOptions: EuiComboBoxOptionOption<string>[]) => {
@@ -59,6 +76,42 @@ export const WfoFieldSelector = ({ handleOnChange, disabled, rule, context }: Fi
59
76
  handleOnChange(selectedValue);
60
77
  };
61
78
 
79
+ // EuiComboBox only auto-selects on Enter/Tab when exactly one option matches the typed
80
+ // text. When several options share the typed prefix, the exact-match option is not picked
81
+ // unless the user arrow-navigates. Patch that with a native keydown listener on the input.
82
+ optionsRef.current = options;
83
+ handleFieldSelectionRef.current = handleFieldSelection;
84
+
85
+ useEffect(() => {
86
+ if (!searchInput) return;
87
+
88
+ const autoSelectExactMatchOption = (event: KeyboardEvent) => {
89
+ if (event.key !== 'Enter' && event.key !== 'Tab') return;
90
+ // Let EuiComboBox handle selection when an option is keyboard-highlighted.
91
+ if (searchInput.getAttribute('aria-activedescendant')) return;
92
+ const typed = searchInput.value.trim();
93
+ if (!typed) return;
94
+ const exactMatch = optionsRef.current.find(
95
+ (option) => !option.disabled && option.label.toLowerCase() === typed.toLowerCase(),
96
+ );
97
+ if (!exactMatch) return;
98
+ if (event.key === 'Enter') event.preventDefault();
99
+ handleFieldSelectionRef.current([exactMatch]);
100
+
101
+ // EuiComboBox keeps its own internal `searchValue` and `isListOpen` state. Clearing
102
+ // the value via the React-recognised native setter + input event suppresses EUI's
103
+ // "typed-but-not-selected" warning icon (the `markAsInvalid` branch in combo_box.js).
104
+ // The synthetic Escape closes the dropdown without disturbing focus.
105
+ const nativeInputValueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;
106
+ nativeInputValueSetter?.call(searchInput, '');
107
+ searchInput.dispatchEvent(new Event('input', { bubbles: true }));
108
+ searchInput.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
109
+ };
110
+
111
+ searchInput.addEventListener('keydown', autoSelectExactMatchOption);
112
+ return () => searchInput.removeEventListener('keydown', autoSelectExactMatchOption);
113
+ }, [searchInput]);
114
+
62
115
  return (
63
116
  <EuiComboBox
64
117
  placeholder={t('searchFieldsPlaceholder')}
@@ -73,6 +126,7 @@ export const WfoFieldSelector = ({ handleOnChange, disabled, rule, context }: Fi
73
126
  setSelectedValue(inputValue);
74
127
  }
75
128
  }}
129
+ inputRef={setSearchInput}
76
130
  singleSelection={{ asPlainText: true }}
77
131
  isLoading={isLoading}
78
132
  isClearable
@@ -1,17 +1,19 @@
1
1
  import React, { useState } from 'react';
2
- import { FullOperator, QueryBuilder, type RuleGroupType } from 'react-querybuilder';
2
+ import { FullOperator, QueryBuilder, type RuleGroupType, generateID } from 'react-querybuilder';
3
3
  import 'react-querybuilder/dist/query-builder.css';
4
4
 
5
5
  import { useTranslations } from 'next-intl';
6
6
 
7
- import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiTextArea } from '@elastic/eui';
7
+ import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
8
8
 
9
- import { SearchParams, WfoTextAnchor } from '@/components';
9
+ import { SearchParams, WfoAutoExpandableTextArea, WfoTextAnchor } from '@/components';
10
10
  import { WfoCombinatorSelector } from '@/components/WfoTable/WfoStructuredSearchTable/WfoCombinatorSelector';
11
11
  import { useWithOrchestratorTheme } from '@/hooks';
12
- import { OperatorDisplay, PathInfo } from '@/types';
12
+ import { OperatorDisplay } from '@/types';
13
+ import type { FieldToOperatorMap } from '@/types';
13
14
 
14
15
  import { WfoFieldSelector } from './WfoFieldSelector';
16
+ import { WfoInlineCombinator } from './WfoInlineCombinator';
15
17
  import { WfoOperatorSelector } from './WfoOperatorSelector';
16
18
  import { WfoRemoveRuleAction } from './WfoRemoveRuleAction';
17
19
  import { WfoRule } from './WfoRule';
@@ -48,7 +50,6 @@ const OPERATOR_MAP: Record<string, OperatorDisplay> = {
48
50
  /* TODO: Add the missing operators
49
51
  ['has_component', 'not_has_component'];
50
52
  */
51
- type FieldPathInfoMap = Map<string, PathInfo>;
52
53
 
53
54
  interface WfoFilterBuilderProps {
54
55
  filterString?: string;
@@ -57,6 +58,9 @@ interface WfoFilterBuilderProps {
57
58
  queryBuilderRuleGroup?: RuleGroupType;
58
59
  onUpdateQueryBuilder: (ruleGroup: RuleGroupType | false) => void;
59
60
  handleSearch: (searchParams?: SearchParams) => void;
61
+ isFilterBuilderVisible: boolean;
62
+ onToggleFilterBuilder: (isVisible: boolean) => void;
63
+ prefilledFieldOptions: FieldToOperatorMap;
60
64
  }
61
65
 
62
66
  const initialRuleGroup: RuleGroupType = {
@@ -65,6 +69,11 @@ const initialRuleGroup: RuleGroupType = {
65
69
  combinator: 'and',
66
70
  };
67
71
 
72
+ const onAddGroupHandler = (ruleGroup: RuleGroupType): RuleGroupType => {
73
+ const [firstRule] = ruleGroup.rules;
74
+ return firstRule ? { ...ruleGroup, rules: [...ruleGroup.rules, { ...firstRule, id: generateID() }] } : ruleGroup;
75
+ };
76
+
68
77
  export const WfoFilterBuilder = ({
69
78
  filterString,
70
79
  onUpdateFilterString,
@@ -72,9 +81,12 @@ export const WfoFilterBuilder = ({
72
81
  queryBuilderRuleGroup = initialRuleGroup,
73
82
  onUpdateQueryBuilder,
74
83
  handleSearch,
84
+ prefilledFieldOptions,
85
+ isFilterBuilderVisible,
86
+ onToggleFilterBuilder,
75
87
  }: WfoFilterBuilderProps) => {
76
- const getOperatorsFromPathInfo = (fieldInfo?: PathInfo): FullOperator[] => {
77
- return (fieldInfo?.operators ?? []).map((operator) => {
88
+ const mapOperatorsToRQBOperatorOptions = (operators?: string[]): FullOperator[] => {
89
+ return (operators ?? []).map((operator) => {
78
90
  const { symbol, description } = OPERATOR_MAP[operator] || { symbol: operator, description: operator };
79
91
  const rqbOperator = SEARCH_OPERATOR_TO_RQB_OPERATOR_MAP[operator] ?? operator;
80
92
  return { name: rqbOperator, label: `${symbol} ${description}`, value: rqbOperator };
@@ -82,34 +94,32 @@ export const WfoFilterBuilder = ({
82
94
  };
83
95
 
84
96
  const t = useTranslations('common');
85
- const { queryBuilderContainerStyles, toggleButtonStyles, textAreaStyles } = useWithOrchestratorTheme(
97
+ const { queryBuilderContainerStyles, toggleButtonStyles } = useWithOrchestratorTheme(
86
98
  getWfoStructuredSearchTableStyles,
87
99
  );
88
- const [isFilterBuilderVisible, setIsFilterBuilderVisible] = useState<boolean>(false);
89
- const [fieldPathInfoMap, setFieldPathInfoMap] = useState<FieldPathInfoMap>(new Map());
100
+ const [fieldToOperatorMap, setFieldToOperatorMap] = useState<FieldToOperatorMap>(prefilledFieldOptions);
90
101
 
91
- const handleFieldSelected = (field: string, pathInfo: PathInfo | undefined) => {
92
- if (pathInfo) {
93
- setFieldPathInfoMap((previousMap) => {
94
- return new Map(previousMap).set(field, pathInfo);
95
- });
96
- }
102
+ const handleFieldSelected = (field: string, operators: string[]) => {
103
+ setFieldToOperatorMap((previousMap) => {
104
+ return new Map(previousMap).set(field, operators);
105
+ });
97
106
  };
98
107
 
99
108
  return (
100
- <EuiFlexGroup css={queryBuilderContainerStyles}>
109
+ <EuiFlexGroup css={isFilterBuilderVisible ? queryBuilderContainerStyles : undefined}>
101
110
  {(isFilterBuilderVisible && (
102
111
  <EuiFlexGroup direction={'column'}>
103
112
  <EuiFlexItem>
104
113
  <QueryBuilder
105
114
  query={queryBuilderRuleGroup}
115
+ enableMountQueryChange={false}
106
116
  onQueryChange={(ruleGroup: RuleGroupType) => {
107
117
  onUpdateQueryBuilder(ruleGroup);
108
118
  }}
109
- context={{ onFieldSelected: handleFieldSelected, fieldPathInfoMap }}
119
+ context={{ onFieldSelected: handleFieldSelected, prefilledFieldOptions }}
110
120
  getOperators={(field) => {
111
- const pathInfo = fieldPathInfoMap.get(field);
112
- return getOperatorsFromPathInfo(pathInfo);
121
+ const operators = fieldToOperatorMap.get(field);
122
+ return mapOperatorsToRQBOperatorOptions(operators);
113
123
  }}
114
124
  controlElements={{
115
125
  fieldSelector: WfoFieldSelector,
@@ -118,27 +128,26 @@ export const WfoFilterBuilder = ({
118
128
  ruleGroup: WfoRuleGroup,
119
129
  rule: WfoRule,
120
130
  combinatorSelector: WfoCombinatorSelector,
131
+ inlineCombinator: WfoInlineCombinator,
121
132
  addRuleAction: null,
122
133
  addGroupAction: null,
123
134
  removeGroupAction: null,
124
135
  removeRuleAction: WfoRemoveRuleAction,
125
136
  }}
126
137
  addRuleToNewGroups
138
+ onAddGroup={onAddGroupHandler}
127
139
  maxLevels={5}
140
+ showCombinatorsBetweenRules
128
141
  />
129
142
  </EuiFlexItem>
130
143
  <EuiFlexItem>
131
- <EuiTextArea
132
- css={textAreaStyles}
144
+ <WfoAutoExpandableTextArea
133
145
  id={'searchbox-textarea'}
134
- value={filterString}
146
+ value={filterString ?? ''}
135
147
  onChange={(e) => {
136
148
  const filterString = e.target.value;
137
149
  onUpdateFilterString(filterString);
138
150
  }}
139
- fullWidth={true}
140
- isClearable={true}
141
- resize={'vertical'}
142
151
  isInvalid={!isValidFilterString}
143
152
  />
144
153
  </EuiFlexItem>
@@ -164,7 +173,7 @@ export const WfoFilterBuilder = ({
164
173
  // we call with ruleGroup: false explictly to
165
174
  // avoid state not having caught up yet when searching
166
175
  handleSearch({ ruleGroup: false });
167
- setIsFilterBuilderVisible(false);
176
+ onToggleFilterBuilder(false);
168
177
  }}
169
178
  />
170
179
  </EuiFlexGroup>
@@ -172,7 +181,7 @@ export const WfoFilterBuilder = ({
172
181
  )) || (
173
182
  <EuiButton
174
183
  css={toggleButtonStyles}
175
- onClick={() => setIsFilterBuilderVisible(true)}
184
+ onClick={() => onToggleFilterBuilder(true)}
176
185
  id={'button-toggle-filter-builder'}
177
186
  data-test-id={'button-toggle-filter-builder'}
178
187
  fill
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import type { CombinatorSelectorProps } from 'react-querybuilder';
3
+
4
+ import { getWfoStructuredSearchTableStyles } from '@/components/WfoTable/WfoStructuredSearchTable/styles';
5
+ import { useWithOrchestratorTheme } from '@/hooks';
6
+
7
+ import { WfoCombinatorSelector } from './WfoCombinatorSelector';
8
+
9
+ export const WfoInlineCombinator = (props: CombinatorSelectorProps) => {
10
+ const { inlineCombinatorStyles } = useWithOrchestratorTheme(getWfoStructuredSearchTableStyles);
11
+
12
+ return (
13
+ <div css={inlineCombinatorStyles}>
14
+ <WfoCombinatorSelector {...props} />
15
+ </div>
16
+ );
17
+ };
@@ -6,15 +6,15 @@ import { useTranslations } from 'next-intl';
6
6
  import { EuiFlexItem } from '@elastic/eui';
7
7
 
8
8
  import { useOrchestratorTheme } from '@/hooks';
9
- import { WfoXMarkSmall } from '@/icons/WfoXMarkSmall';
9
+ import { WfoTrashFilled } from '@/icons';
10
10
 
11
11
  export const WfoRemoveRuleAction = (props: ActionProps) => {
12
12
  const { theme } = useOrchestratorTheme();
13
13
 
14
14
  const t = useTranslations('search.page');
15
15
  return (
16
- <EuiFlexItem onClick={props.handleOnClick} css={{ cursor: 'pointer' }}>
17
- <WfoXMarkSmall color={theme.colors.primary} aria-label={t('removeRule')} />
16
+ <EuiFlexItem onClick={props.handleOnClick} css={{ cursor: 'pointer', paddingRight: theme.size.s }}>
17
+ <WfoTrashFilled color={theme.colors.textPrimary} aria-label={t('removeRule')} />
18
18
  </EuiFlexItem>
19
19
  );
20
20
  };
@@ -20,6 +20,7 @@ export const WfoRuleGroup = (props: RuleGroupProps) => {
20
20
  ruleGroupContainerWhiteStyles,
21
21
  innerGroupContainerWhiteStyles,
22
22
  innerGroupContainerBlueStyles,
23
+ ruleGroupBodyGridStyles,
23
24
  } = useWithOrchestratorTheme(getWfoStructuredSearchTableStyles);
24
25
  const getGroupContainerStyles = () => {
25
26
  if (ruleGroupProps.path.length % 2) {
@@ -32,16 +33,18 @@ export const WfoRuleGroup = (props: RuleGroupProps) => {
32
33
  const { addRule, path, schema, disabled, ruleGroup, addGroup } = ruleGroupProps;
33
34
 
34
35
  return (
35
- <EuiFlexGroup gutterSize={'none'} alignItems="center">
36
+ <EuiFlexGroup gutterSize={'none'} responsive={false} alignItems="center">
36
37
  <EuiFlexItem>
37
- <EuiFlexGroup direction="column" gutterSize="s" css={getGroupContainerStyles()}>
38
+ <EuiFlexGroup direction="column" css={getGroupContainerStyles()}>
38
39
  <EuiFlexItem>
39
40
  <EuiFlexGroup gutterSize="none">
40
41
  <RuleGroupHeaderComponents {...ruleGroupProps} />
41
42
  </EuiFlexGroup>
42
43
  </EuiFlexItem>
43
44
  <EuiFlexItem>
44
- <RuleGroupBodyComponents {...ruleGroupProps} />
45
+ <div css={ruleGroupBodyGridStyles}>
46
+ <RuleGroupBodyComponents {...ruleGroupProps} />
47
+ </div>
45
48
  </EuiFlexItem>
46
49
  <EuiFlexItem>
47
50
  <WfoAddRuleAction
@@ -0,0 +1,56 @@
1
+ import React from 'react';
2
+
3
+ import { useTranslations } from 'next-intl';
4
+
5
+ import { EuiButtonIcon, EuiFieldSearch, EuiFlexItem, EuiFormRow } from '@elastic/eui';
6
+
7
+ import { useWithOrchestratorTheme } from '@/hooks';
8
+ import { getFormFieldsBaseStyle } from '@/theme';
9
+
10
+ export type SearchFieldWithActionsProps = {
11
+ queryText?: string;
12
+ onChangeQueryText: (queryText: string) => void;
13
+ onSearchQueryText: (queryText: string) => void;
14
+ onShowInformation: () => void;
15
+ onShowTableSettings: () => void;
16
+ };
17
+
18
+ // Search field with the info and table-settings actions, rendered as EuiFlexItems inside an EuiFlexGroup.
19
+ export const WfoSearchFieldWithActions = ({
20
+ queryText,
21
+ onChangeQueryText,
22
+ onSearchQueryText,
23
+ onShowInformation,
24
+ onShowTableSettings,
25
+ }: SearchFieldWithActionsProps) => {
26
+ const t = useTranslations('common');
27
+ const { formFieldBaseStyle } = useWithOrchestratorTheme(getFormFieldsBaseStyle);
28
+
29
+ return (
30
+ <>
31
+ <EuiFlexItem>
32
+ <EuiFormRow fullWidth>
33
+ <EuiFieldSearch
34
+ css={formFieldBaseStyle}
35
+ value={queryText}
36
+ placeholder={`${t('search')}...`}
37
+ onChange={(e) => onChangeQueryText(e.target.value)}
38
+ onSearch={(queryText) => onSearchQueryText(queryText)}
39
+ fullWidth
40
+ />
41
+ </EuiFormRow>
42
+ </EuiFlexItem>
43
+ <EuiFlexItem grow={false}>
44
+ <EuiButtonIcon
45
+ onClick={onShowInformation}
46
+ iconSize={'l'}
47
+ iconType={'info'}
48
+ aria-label={t('searchModalTitle')}
49
+ />
50
+ </EuiFlexItem>
51
+ <EuiFlexItem grow={false}>
52
+ <EuiButtonIcon onClick={onShowTableSettings} iconSize={'l'} iconType={'gear'} aria-label={t('tableSettings')} />
53
+ </EuiFlexItem>
54
+ </>
55
+ );
56
+ };