@orchestrator-ui/orchestrator-ui-components 3.5.2 → 3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchestrator-ui/orchestrator-ui-components",
3
- "version": "3.5.2",
3
+ "version": "3.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": {
@@ -3,10 +3,16 @@ import React, { FC, useState } from 'react';
3
3
  import { useTranslations } from 'next-intl';
4
4
  import { useRouter } from 'next/router';
5
5
 
6
- import { EuiHorizontalRule, EuiSideNav, EuiSpacer } from '@elastic/eui';
6
+ import {
7
+ EuiHorizontalRule,
8
+ EuiSideNav,
9
+ EuiSpacer,
10
+ htmlIdGenerator,
11
+ } from '@elastic/eui';
7
12
  import { EuiSideNavItemType } from '@elastic/eui/src/components/side_nav/side_nav_types';
8
13
 
9
14
  import { WfoIsAllowedToRender, menuItemIsAllowed } from '@/components';
15
+ import { WfoMenuItemLink } from '@/components';
10
16
  import { getMenuStyles } from '@/components/WfoPageTemplate/WfoSidebar/styles';
11
17
  import { WfoStartWorkflowButtonComboBox } from '@/components/WfoStartButton/WfoStartWorkflowComboBox';
12
18
  import { PolicyResource } from '@/configuration/policy-resources';
@@ -26,7 +32,6 @@ import {
26
32
  PATH_WORKFLOWS,
27
33
  } from '../paths';
28
34
  import { WfoCopyright } from './WfoCopyright';
29
- import { WfoMenuItemLink } from './WfoMenuLink';
30
35
 
31
36
  export const urlPolicyMap = new Map<string, PolicyResource>([
32
37
  [PATH_WORKFLOWS, PolicyResource.NAVIGATION_WORKFLOWS],
@@ -36,11 +41,11 @@ export const urlPolicyMap = new Map<string, PolicyResource>([
36
41
  [PATH_SETTINGS, PolicyResource.NAVIGATION_SETTINGS],
37
42
  ]);
38
43
 
39
- export const sideNavMenuDivider: EuiSideNavItemType<object> = {
44
+ export const createSideNavDivider = (): EuiSideNavItemType<object> => ({
40
45
  name: '',
41
- id: 'menuDivider',
46
+ id: htmlIdGenerator('menuDivider')(),
42
47
  renderItem: () => <EuiHorizontalRule margin="xs" />,
43
- };
48
+ });
44
49
 
45
50
  export type WfoSidebarProps = {
46
51
  overrideMenuItems?: (
@@ -4,8 +4,10 @@ import { useTranslations } from 'next-intl';
4
4
 
5
5
  import { EuiFlexItem, EuiPanel, EuiSpacer, EuiText } from '@elastic/eui';
6
6
 
7
- import { WfoEngineStatusButton } from './WfoEngineStatusButton';
8
- import { WfoResetTextSearchIndexButton } from './WfoResetTextSearchIndexButton';
7
+ import {
8
+ WfoEngineStatusButton,
9
+ WfoResetTextSearchIndexButton,
10
+ } from '@/components';
9
11
 
10
12
  export const WfoModifySettings = () => {
11
13
  const t = useTranslations('settings.page');
@@ -22,49 +22,62 @@ export const WfoDragHandler: FC<WfoDragHandlerProps> = ({
22
22
  onUpdateColumWidth,
23
23
  }) => {
24
24
  const [position, setPosition] = useState({ x: 0, y: 0 });
25
+ const [isDragging, setIsDragging] = useState(false);
26
+ const { dragAndDropStyle } = useWithOrchestratorTheme(getWfoTableStyles);
27
+
28
+ const resetPosition = () => {
29
+ setPosition({ x: 0, y: 0 });
30
+ };
31
+
32
+ const onStart: DraggableEventHandler = () => {
33
+ setIsDragging(false);
34
+ };
25
35
 
26
36
  const onDrag: DraggableEventHandler = (_, data) => {
37
+ setIsDragging(true);
27
38
  setPosition({ x: data.x, y: data.y });
28
39
  };
29
40
 
30
- const resetPosition = () => {
31
- setPosition({ x: 0, y: 0 });
32
- };
41
+ const onStop: DraggableEventHandler = (_, data) => {
42
+ if (headerRowRef.current && isDragging) {
43
+ const newWidth = startWidth + data.x;
33
44
 
34
- const { dragAndDropStyle } = useWithOrchestratorTheme(getWfoTableStyles);
45
+ onUpdateColumWidth(
46
+ fieldName,
47
+ newWidth > MINIMUM_COLUMN_WIDTH
48
+ ? newWidth
49
+ : MINIMUM_COLUMN_WIDTH,
50
+ );
51
+ resetPosition();
52
+ setIsDragging(false);
53
+ }
54
+ };
35
55
 
36
56
  const thElement =
37
57
  headerRowRef.current &&
38
58
  (headerRowRef.current.querySelector(
39
59
  `th[data-field-name="${fieldName}"]`,
40
60
  ) as HTMLTableCellElement);
61
+
41
62
  const startWidth =
42
63
  thElement?.getBoundingClientRect().width ?? MINIMUM_COLUMN_WIDTH;
43
64
 
65
+ const bounds = {
66
+ left: MINIMUM_COLUMN_WIDTH - startWidth,
67
+ top: 0,
68
+ bottom: 0,
69
+ };
70
+
44
71
  return (
45
72
  <div>
46
73
  <Draggable
74
+ allowAnyClick={false}
47
75
  axis="x"
48
76
  position={position}
77
+ onStart={onStart}
49
78
  onDrag={onDrag}
50
- bounds={{
51
- left: MINIMUM_COLUMN_WIDTH - startWidth,
52
- top: 0,
53
- bottom: 0,
54
- }}
55
- onStop={(_, data) => {
56
- if (headerRowRef.current) {
57
- const newWidth = startWidth + data.x;
58
-
59
- onUpdateColumWidth(
60
- fieldName,
61
- newWidth > MINIMUM_COLUMN_WIDTH
62
- ? newWidth
63
- : MINIMUM_COLUMN_WIDTH,
64
- );
65
- resetPosition();
66
- }
67
- }}
79
+ bounds={bounds}
80
+ onStop={onStop}
68
81
  >
69
82
  <div css={dragAndDropStyle} />
70
83
  </Draggable>
@@ -102,6 +102,7 @@ export const WfoGroupedTable = <T extends object>({
102
102
  uniqueRowIdToExpandedRowMap,
103
103
  }}
104
104
  onRowClick={({ groupName }) => toggleExpandedRow(groupName)}
105
+ appendFillerColumn={false}
105
106
  />
106
107
  </>
107
108
  );
@@ -16,7 +16,7 @@ import { DEFAULT_PAGE_SIZES } from '../utils/constants';
16
16
  import { WfoTableDataRows } from './WfoTableDataRows';
17
17
  import { WfoTableHeaderRow } from './WfoTableHeaderRow';
18
18
  import { getWfoTableStyles } from './styles';
19
- import { getSortedVisibleColumns } from './utils';
19
+ import { getColumnWidthsFromConfig, getSortedVisibleColumns } from './utils';
20
20
 
21
21
  export type Pagination = {
22
22
  pageSize: number;
@@ -99,10 +99,11 @@ export type WfoTableProps<T extends object> = {
99
99
  onRowClick?: (row: T) => void;
100
100
  onUpdateDataSorting?: (updatedDataSorting: WfoDataSorting<T>) => void;
101
101
  onUpdateDataSearch?: (updatedDataSearch: WfoDataSearch<T>) => void;
102
+ appendFillerColumn?: boolean;
102
103
  className?: string;
103
104
  };
104
105
 
105
- type LocalColumnWidths = {
106
+ export type LocalColumnWidths = {
106
107
  [key: string]: string;
107
108
  };
108
109
 
@@ -119,25 +120,24 @@ export const WfoTable = <T extends object>({
119
120
  onUpdateDataSorting,
120
121
  onUpdateDataSearch,
121
122
  onRowClick,
123
+ appendFillerColumn = true,
122
124
  className,
123
125
  }: WfoTableProps<T>) => {
124
- const getColumnWidthsFromConfig = (
125
- columnConfig: WfoTableColumnConfig<T>,
126
- ): LocalColumnWidths => {
127
- return Object.entries(columnConfig).reduce(
128
- (columnWidths, [key, config]) => {
129
- if (config.columnType === ColumnType.DATA) {
130
- columnWidths[key] = config.width ?? 'auto';
131
- }
132
- return columnWidths;
133
- },
134
- {} as LocalColumnWidths,
135
- );
136
- };
137
-
138
126
  const [localColumnWidths, setLocalColumnWidths] =
139
127
  useState<LocalColumnWidths>(getColumnWidthsFromConfig(columnConfig));
140
128
 
129
+ const columnConfigWithFiller: WfoTableColumnConfig<T> = appendFillerColumn
130
+ ? {
131
+ ...columnConfig,
132
+ filler: {
133
+ columnType: ColumnType.CONTROL,
134
+ label: '',
135
+ width: '100%',
136
+ renderControl: () => null,
137
+ },
138
+ }
139
+ : columnConfig;
140
+
141
141
  const {
142
142
  tableContainerStyle,
143
143
  tableStyle,
@@ -151,7 +151,7 @@ export const WfoTable = <T extends object>({
151
151
  const t = useTranslations('common');
152
152
 
153
153
  const sortedVisibleColumns = getSortedVisibleColumns(
154
- columnConfig,
154
+ columnConfigWithFiller,
155
155
  columnOrder,
156
156
  hiddenColumns,
157
157
  );
@@ -166,7 +166,7 @@ export const WfoTable = <T extends object>({
166
166
  };
167
167
 
168
168
  const configWithLocalWidths: WfoTableColumnConfig<T> = Object.entries(
169
- columnConfig,
169
+ columnConfigWithFiller,
170
170
  ).reduce((mergedConfig, [fieldName, fieldConfig]) => {
171
171
  const key = fieldName as keyof WfoTableColumnConfig<T>;
172
172
  if (fieldConfig.columnType === ColumnType.DATA) {
@@ -37,7 +37,7 @@ export const getWfoTableStyles = ({ theme, isDarkThemeActive }: WfoTheme) => {
37
37
  });
38
38
 
39
39
  const tableStyle = css({
40
- width: 'auto',
40
+ width: '100%',
41
41
  });
42
42
 
43
43
  const headerStyle = css({
@@ -136,7 +136,7 @@ export const getWfoTableStyles = ({ theme, isDarkThemeActive }: WfoTheme) => {
136
136
  position: 'absolute',
137
137
  height: '100%',
138
138
  zIndex: theme.levels.menu,
139
- '&:hover, &:active': {
139
+ '&:active, &:focus': {
140
140
  transition: 'background-color 0.15s',
141
141
  backgroundColor: isDarkThemeActive
142
142
  ? theme.colors.mediumShade
@@ -152,7 +152,7 @@ export const getWfoTableStyles = ({ theme, isDarkThemeActive }: WfoTheme) => {
152
152
  opacity: 0.6,
153
153
  zIndex: theme.levels.navigation,
154
154
  },
155
- '&:hover::after, &:active::after': {
155
+ '&:active::after': {
156
156
  transition: 'opacity 0.15s',
157
157
  opacity: 0,
158
158
  },
@@ -1,4 +1,4 @@
1
- import { TableColumnKeys } from '@/components';
1
+ import { LocalColumnWidths, TableColumnKeys } from '@/components';
2
2
  import { SortOrder } from '@/types';
3
3
  import { toObjectWithSortedKeys } from '@/utils';
4
4
 
@@ -80,3 +80,16 @@ export function mapSortableAndFilterableValuesToTableColumnConfig<
80
80
 
81
81
  return Object.fromEntries(tableColumnConfigUpdatedEntries);
82
82
  }
83
+
84
+ export const getColumnWidthsFromConfig = <T extends object>(
85
+ columnConfig: WfoTableColumnConfig<T>,
86
+ ): LocalColumnWidths => {
87
+ const columnEntries = Object.entries(columnConfig);
88
+
89
+ return columnEntries.reduce((columnWidths, [key, config]) => {
90
+ if (config.columnType === ColumnType.DATA) {
91
+ columnWidths[key] = config.width ?? 'auto';
92
+ }
93
+ return columnWidths;
94
+ }, {} as LocalColumnWidths);
95
+ };
@@ -1 +1 @@
1
- export const ORCHESTRATOR_UI_LIBRARY_VERSION = '3.5.2';
1
+ export const ORCHESTRATOR_UI_LIBRARY_VERSION = '3.6.0';
@@ -24,8 +24,8 @@ export const WfoQuestionCircle: FC<WfoIconProps> = ({
24
24
  >
25
25
  <g id="icon/play-circle" fill="none" fillRule="nonzero">
26
26
  <path
27
- stroke-linecap="round"
28
- stroke-linejoin="round"
27
+ strokeLinecap="round"
28
+ strokeLinejoin="round"
29
29
  d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 5.25h.008v.008H12v-.008Z"
30
30
  />
31
31
  </g>
@@ -1,4 +1,8 @@
1
- import { optionalArrayMapper, toOptionalArrayEntry } from './optionalArray';
1
+ import {
2
+ optionalArrayMapper,
3
+ toOptionalArrayEntries,
4
+ toOptionalArrayEntry,
5
+ } from './optionalArray';
2
6
 
3
7
  describe('toOptionalArrayEntry', () => {
4
8
  const testInput = { testField: 'testValue' };
@@ -16,6 +20,42 @@ describe('toOptionalArrayEntry', () => {
16
20
  });
17
21
  });
18
22
 
23
+ describe('toOptionalArrayEntries', () => {
24
+ test('returns data as an array when condition is true and data is not an array', () => {
25
+ const result = toOptionalArrayEntries('singleItem', true);
26
+ expect(result).toEqual(['singleItem']);
27
+ });
28
+
29
+ test('returns data as it is when condition is true and data is already an array', () => {
30
+ const result = toOptionalArrayEntries(['item1', 'item2'], true);
31
+ expect(result).toEqual(['item1', 'item2']);
32
+ });
33
+
34
+ test('returns an empty array when condition is false regardless of data type', () => {
35
+ const resultWithSingleItem = toOptionalArrayEntries(
36
+ 'singleItem',
37
+ false,
38
+ );
39
+ const resultWithArray = toOptionalArrayEntries(
40
+ ['item1', 'item2'],
41
+ false,
42
+ );
43
+
44
+ expect(resultWithSingleItem).toEqual([]);
45
+ expect(resultWithArray).toEqual([]);
46
+ });
47
+
48
+ test('does not mutate the original data', () => {
49
+ const singleItem = 'singleItem';
50
+ const arrayData = ['item1', 'item2'];
51
+ toOptionalArrayEntries(singleItem, true);
52
+ toOptionalArrayEntries(arrayData, true);
53
+
54
+ expect(singleItem).toBe('singleItem');
55
+ expect(arrayData).toEqual(['item1', 'item2']);
56
+ });
57
+ });
58
+
19
59
  describe('optionalArrayMapper', () => {
20
60
  it('applies the mapper when data is defined', () => {
21
61
  const testData = [{ testField: 'testValue' }];
@@ -3,6 +3,11 @@ export const toOptionalArrayEntry = <T>(
3
3
  condition: boolean,
4
4
  ): [T] | [] => (condition ? [data] : []);
5
5
 
6
+ export const toOptionalArrayEntries = <T>(
7
+ data: T | T[],
8
+ condition: boolean,
9
+ ): T[] => (condition ? (Array.isArray(data) ? data : [data]) : []);
10
+
6
11
  export const optionalArrayMapper = <T, U>(
7
12
  data: T[] | undefined = [],
8
13
  mapper: (input: T) => U,