@orchestrator-ui/orchestrator-ui-components 1.26.0 → 1.27.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 (41) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/.turbo/turbo-test.log +34 -33
  4. package/CHANGELOG.md +15 -0
  5. package/dist/index.d.ts +76 -66
  6. package/dist/index.js +599 -537
  7. package/jest.config.cjs +13 -2
  8. package/package.json +1 -1
  9. package/src/components/WfoForms/UserInputForm.tsx +18 -13
  10. package/src/components/WfoForms/UserInputFormWizard.tsx +3 -3
  11. package/src/components/WfoPageTemplate/WfoBreadcrumbs/WfoBreadcrumbs.tsx +52 -10
  12. package/src/components/WfoPageTemplate/WfoPageHeader/WfoPageHeader.tsx +1 -11
  13. package/src/components/WfoPageTemplate/WfoPageTemplate/WfoPageTemplate.tsx +5 -4
  14. package/src/components/WfoSubscription/WfoInSyncField.tsx +1 -1
  15. package/src/components/WfoSubscription/WfoSubscriptionDetailTree.tsx +4 -1
  16. package/src/components/WfoTree/WfoTree.tsx +4 -3
  17. package/src/components/WfoTree/WfoTreeBranch.tsx +10 -3
  18. package/src/components/WfoTree/treeUtils.ts +13 -0
  19. package/src/components/confirmationDialog/WfoConfirmationDialog.tsx +61 -68
  20. package/src/contexts/ConfirmationDialogProvider.tsx +57 -60
  21. package/src/messages/en-GB.json +3 -1
  22. package/src/messages/nl-NL.json +3 -1
  23. package/src/pages/processes/WfoProcessDetail.tsx +3 -3
  24. package/src/pages/tasks/WfoTasksListPage.tsx +1 -1
  25. package/src/rtk/api.ts +5 -21
  26. package/src/rtk/endpoints/processDetail.ts +11 -11
  27. package/src/rtk/endpoints/processList.ts +8 -2
  28. package/src/rtk/endpoints/processListSummary.ts +7 -3
  29. package/src/rtk/endpoints/processStatusCounts.ts +4 -3
  30. package/src/rtk/endpoints/settings.ts +5 -5
  31. package/src/rtk/endpoints/startOptions.ts +4 -0
  32. package/src/rtk/endpoints/streamMessages.ts +13 -45
  33. package/src/rtk/endpoints/subscriptionActions.ts +1 -2
  34. package/src/rtk/endpoints/subscriptionDetail.ts +12 -7
  35. package/src/rtk/endpoints/subscriptionInUseByRelationsList.ts +4 -2
  36. package/src/rtk/endpoints/subscriptionList.ts +4 -2
  37. package/src/rtk/endpoints/subscriptionListSummary.ts +4 -2
  38. package/src/rtk/endpoints/translations.ts +1 -2
  39. package/src/types/types.ts +11 -0
  40. package/src/utils/cacheTag.spec.ts +14 -0
  41. package/src/utils/cacheTag.ts +11 -0
package/jest.config.cjs CHANGED
@@ -1,6 +1,17 @@
1
1
  const base = require('@orchestrator-ui/jest-config/jest-base.config.js');
2
+ const nextJest = require('next/jest');
2
3
 
3
- module.exports = {
4
+ const createJestConfig = nextJest({
5
+ // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
6
+ dir: './',
7
+ });
8
+
9
+ // The entry for "uuid" in the moduleNameMapper can be removed when EUI updates the dependency version to 9.0.0 or higher.
10
+ // https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md#900-2022-09-05
11
+ const customJestConfig = {
4
12
  ...base,
5
- displayName: 'Orchestrator UI Components Tests',
13
+ displayName: 'Wfo-UI Tests',
14
+ moduleNameMapper: { '^uuid$': 'uuid' },
6
15
  };
16
+
17
+ module.exports = createJestConfig(customJestConfig);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchestrator-ui/orchestrator-ui-components",
3
- "version": "1.26.0",
3
+ "version": "1.27.1",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Library of UI Components used to display the workflow orchestrator frontend",
6
6
  "author": {
@@ -33,7 +33,7 @@ import {
33
33
  EuiHorizontalRule,
34
34
  } from '@elastic/eui';
35
35
 
36
- import { ConfirmDialogActions, ConfirmationDialogContext } from '@/contexts';
36
+ import { ConfirmDialogHandler, ConfirmationDialogContext } from '@/contexts';
37
37
  import { useOrchestratorTheme } from '@/hooks';
38
38
  import { WfoPlayFill } from '@/icons';
39
39
  import { FormValidationError, ValidationError } from '@/types/forms';
@@ -51,8 +51,8 @@ interface IProps {
51
51
  router: NextRouter;
52
52
  stepUserInput: JSONSchema6;
53
53
  validSubmit: (userInput: { [index: string]: unknown }) => Promise<unknown>;
54
- cancel?: ConfirmDialogActions['closeConfirmDialog'];
55
- previous: ConfirmDialogActions['closeConfirmDialog'];
54
+ cancel?: ConfirmDialogHandler;
55
+ previous: ConfirmDialogHandler;
56
56
  hasNext?: boolean;
57
57
  hasPrev?: boolean;
58
58
  userInput: object;
@@ -423,6 +423,7 @@ export function WfoUserInputForm({
423
423
  isResuming = false,
424
424
  }: IProps) {
425
425
  const t = useTranslations('pydanticForms.userInputForm');
426
+ const tDialog = useTranslations('confirmationDialog');
426
427
  const { theme } = useOrchestratorTheme();
427
428
  const { showConfirmDialog } = useContext(ConfirmationDialogContext);
428
429
  const [processing, setProcessing] = useState<boolean>(false);
@@ -430,15 +431,21 @@ export function WfoUserInputForm({
430
431
  const [rootErrors, setRootErrors] = useState<string[]>([]);
431
432
 
432
433
  const openLeavePageDialog = (
433
- leaveAction: ConfirmDialogActions['closeConfirmDialog'],
434
+ leaveAction: ConfirmDialogHandler,
434
435
  leaveQuestion?: string,
435
436
  ) => {
437
+ const question = leaveQuestion || tDialog('leavePage');
438
+ const subQuestion = leaveQuestion ? tDialog('leavePageSub') : '';
439
+ const cancelButtonText = tDialog('stay');
440
+ const confirmButtonText = tDialog('leave');
441
+
436
442
  return () =>
437
443
  showConfirmDialog({
438
- question: leaveQuestion || '',
439
- confirmAction: () => {},
440
- cancelAction: leaveAction,
441
- leavePage: true,
444
+ question,
445
+ subQuestion,
446
+ onConfirm: leaveAction,
447
+ cancelButtonText,
448
+ confirmButtonText,
442
449
  });
443
450
  };
444
451
 
@@ -496,17 +503,15 @@ export function WfoUserInputForm({
496
503
  | React.MouseEvent<HTMLButtonElement>
497
504
  | React.MouseEvent<HTMLDivElement, MouseEvent>,
498
505
  question: string | undefined,
499
- confirm: ConfirmDialogActions['closeConfirmDialog'],
506
+ confirm: () => void,
500
507
  ) => {
501
508
  if (!question) {
502
- return confirm(e);
509
+ return confirm();
503
510
  }
504
511
 
505
512
  showConfirmDialog({
506
513
  question: question,
507
- confirmAction: confirm,
508
- cancelAction: () => {},
509
- leavePage: false,
514
+ onConfirm: confirm,
510
515
  });
511
516
  };
512
517
 
@@ -17,7 +17,7 @@ import React, { useEffect, useState } from 'react';
17
17
  import { useRouter } from 'next/router';
18
18
  import hash from 'object-hash';
19
19
 
20
- import { ConfirmDialogActions } from '@/contexts';
20
+ import type { ConfirmDialogHandler } from '@/contexts';
21
21
  import { HttpStatus, handlePromiseErrorWithCallback } from '@/rtk';
22
22
  import { FormNotCompleteResponse, InputForm } from '@/types/forms';
23
23
 
@@ -31,7 +31,7 @@ interface Form {
31
31
  interface UserInputFormWizardProps {
32
32
  stepUserInput: InputForm;
33
33
  stepSubmit: (processInput: object[]) => Promise<unknown>;
34
- cancel?: () => void;
34
+ cancel?: ConfirmDialogHandler;
35
35
  isTask: boolean;
36
36
  hasNext?: boolean;
37
37
  isResuming?: boolean;
@@ -62,7 +62,7 @@ export const UserInputFormWizard = ({
62
62
  setForms([{ form: stepUserInput, hasNext: hasNext }]);
63
63
  }, [hasNext, stepUserInput]);
64
64
 
65
- const previous: ConfirmDialogActions['closeConfirmDialog'] = (e) => {
65
+ const previous: ConfirmDialogHandler = (e) => {
66
66
  if (e) {
67
67
  stop(e);
68
68
  }
@@ -1,17 +1,35 @@
1
1
  import React from 'react';
2
2
 
3
+ import { useTranslations } from 'next-intl';
3
4
  import { useRouter } from 'next/router';
4
5
 
5
- import { EuiBreadcrumb, EuiBreadcrumbs, EuiSpacer } from '@elastic/eui';
6
+ import {
7
+ EuiBreadcrumb,
8
+ EuiBreadcrumbs,
9
+ EuiButtonIcon,
10
+ EuiFlexGroup,
11
+ EuiFlexItem,
12
+ EuiSpacer,
13
+ } from '@elastic/eui';
6
14
 
7
- import { isUuid4, removeSuffix, upperCaseFirstChar } from '../../../utils';
15
+ import { useOrchestratorTheme } from '@/hooks';
16
+ import { WfoSideMenu } from '@/icons';
17
+ import { isUuid4, removeSuffix, upperCaseFirstChar } from '@/utils';
8
18
 
9
- export const WfoBreadcrumbs = () => {
19
+ interface WfoBreadcrumbsProps {
20
+ handleSideMenuClick: () => void;
21
+ }
22
+
23
+ export const WfoBreadcrumbs = ({
24
+ handleSideMenuClick,
25
+ }: WfoBreadcrumbsProps) => {
10
26
  const router = useRouter();
27
+ const { theme } = useOrchestratorTheme();
28
+ const t = useTranslations('main');
11
29
  // Setup initial breadcrumbs with navigation to home
12
30
  const breadcrumbs: EuiBreadcrumb[] = [
13
31
  {
14
- text: 'Start',
32
+ text: t('start'),
15
33
  href: '/',
16
34
  onClick: (e) => {
17
35
  e.preventDefault();
@@ -29,7 +47,6 @@ export const WfoBreadcrumbs = () => {
29
47
  // first remove the suffix, like ?activeTab=....
30
48
  const _p = removeSuffix(p);
31
49
  const text = isUuid4(_p) ? _p : upperCaseFirstChar(_p);
32
- // eslint-disable-next-line no-console
33
50
 
34
51
  breadcrumbs.push({
35
52
  text: text,
@@ -44,11 +61,36 @@ export const WfoBreadcrumbs = () => {
44
61
 
45
62
  return (
46
63
  <>
47
- <EuiBreadcrumbs
48
- breadcrumbs={breadcrumbs}
49
- truncate={false}
50
- aria-label="Current page"
51
- />
64
+ <EuiFlexGroup
65
+ direction="row"
66
+ alignItems="center"
67
+ justifyContent="flexStart"
68
+ gutterSize="none"
69
+ >
70
+ <EuiFlexItem grow={0}>
71
+ <EuiButtonIcon
72
+ aria-label={t('ariaLabelToggleSideMenu')}
73
+ display="empty"
74
+ iconType={() => (
75
+ <WfoSideMenu color={theme.colors.subduedText} />
76
+ )}
77
+ color="text"
78
+ css={{
79
+ width: `${theme.size.l}px`,
80
+ height: `${theme.size.l}px`,
81
+ marginRight: `${theme.base * 0.375}px`,
82
+ }}
83
+ onClick={handleSideMenuClick}
84
+ />
85
+ </EuiFlexItem>
86
+ <EuiFlexItem>
87
+ <EuiBreadcrumbs
88
+ breadcrumbs={breadcrumbs}
89
+ truncate={false}
90
+ aria-label={t('ariaLabelCurrentPage')}
91
+ />
92
+ </EuiFlexItem>
93
+ </EuiFlexGroup>
52
94
  <EuiSpacer size="m" />
53
95
  </>
54
96
  );
@@ -25,14 +25,13 @@ import {
25
25
  useOrchestratorTheme,
26
26
  useWithOrchestratorTheme,
27
27
  } from '@/hooks';
28
- import { WfoLogoutIcon, WfoSideMenu } from '@/icons';
28
+ import { WfoLogoutIcon } from '@/icons';
29
29
  import { ColorModes } from '@/types';
30
30
 
31
31
  export interface WfoPageHeaderProps {
32
32
  // todo: should be part of theme!
33
33
  navigationHeight: number;
34
34
  getAppLogo: (navigationHeight: number) => ReactElement;
35
- handleSideMenuClick: () => void;
36
35
  handleLogoutClick: () => void;
37
36
  onThemeSwitch: (theme: EuiThemeColorMode) => void;
38
37
  }
@@ -40,7 +39,6 @@ export interface WfoPageHeaderProps {
40
39
  export const WfoPageHeader: FC<WfoPageHeaderProps> = ({
41
40
  navigationHeight,
42
41
  getAppLogo,
43
- handleSideMenuClick,
44
42
  handleLogoutClick,
45
43
  onThemeSwitch,
46
44
  }) => {
@@ -62,14 +60,6 @@ export const WfoPageHeader: FC<WfoPageHeaderProps> = ({
62
60
  <EuiHeaderSectionItem>
63
61
  <WfoEnvironmentBadge />
64
62
  </EuiHeaderSectionItem>
65
-
66
- <EuiButtonIcon
67
- aria-label="Show/Hide side menu"
68
- display="empty"
69
- iconType={() => <WfoSideMenu color={theme.colors.ghost} />}
70
- css={{ width: 48, height: 48, marginLeft: 10 }}
71
- onClick={handleSideMenuClick}
72
- />
73
63
  </EuiHeaderSection>
74
64
 
75
65
  <EuiHeaderSection>
@@ -35,9 +35,6 @@ export const WfoPageTemplate: FC<WfoPageTemplateProps> = ({
35
35
  <WfoPageHeader
36
36
  getAppLogo={getAppLogo}
37
37
  navigationHeight={navigationHeight}
38
- handleSideMenuClick={() =>
39
- setIsSideMenuVisible((prevState) => !prevState)
40
- }
41
38
  handleLogoutClick={signOut}
42
39
  onThemeSwitch={onThemeSwitch}
43
40
  />
@@ -64,7 +61,11 @@ export const WfoPageTemplate: FC<WfoPageTemplateProps> = ({
64
61
  backgroundColor: theme.colors.emptyShade,
65
62
  }}
66
63
  >
67
- <WfoBreadcrumbs />
64
+ <WfoBreadcrumbs
65
+ handleSideMenuClick={() =>
66
+ setIsSideMenuVisible((prevState) => !prevState)
67
+ }
68
+ />
68
69
  {children}
69
70
  </EuiPageTemplate.Section>
70
71
  </EuiPageTemplate>
@@ -63,7 +63,7 @@ export const WfoInSyncField = ({ subscriptionDetail }: WfoInSyncFieldProps) => {
63
63
  const confirmSetInSync = () => {
64
64
  showConfirmDialog({
65
65
  question: t('setInSyncQuestion'),
66
- confirmAction: () => {
66
+ onConfirm: () => {
67
67
  setInSyncAction();
68
68
  },
69
69
  });
@@ -127,7 +127,10 @@ export const WfoSubscriptionDetailTree = ({
127
127
  <EuiFlexItem grow={true}>
128
128
  {!tree && <WfoLoading />}
129
129
  {tree && (
130
- <WfoTree data={[tree]} depthList={depthList} />
130
+ <WfoTree
131
+ treeBlocks={[tree]}
132
+ depthList={depthList}
133
+ />
131
134
  )}
132
135
  </EuiFlexItem>
133
136
  </EuiFlexGroup>
@@ -3,13 +3,14 @@ import React, { FC, useEffect } from 'react';
3
3
  import { TreeContext, TreeContextType } from '../../contexts';
4
4
  import { TreeBlock } from '../../types';
5
5
  import { WfoTreeBranch } from './WfoTreeBranch';
6
+ import { sortTreeBlockByLabel } from './treeUtils';
6
7
 
7
8
  type WfoTreeProps = {
8
- data: TreeBlock[];
9
+ treeBlocks: TreeBlock[];
9
10
  depthList: number[];
10
11
  };
11
12
 
12
- export const WfoTree: FC<WfoTreeProps> = ({ data, depthList }) => {
13
+ export const WfoTree: FC<WfoTreeProps> = ({ treeBlocks, depthList }) => {
13
14
  const { setDepths } = React.useContext(TreeContext) as TreeContextType;
14
15
 
15
16
  useEffect(() => {
@@ -19,7 +20,7 @@ export const WfoTree: FC<WfoTreeProps> = ({ data, depthList }) => {
19
20
 
20
21
  return (
21
22
  <div style={{ width: '500px' }}>
22
- {data.map((item) => (
23
+ {[...treeBlocks].sort(sortTreeBlockByLabel).map((item) => (
23
24
  <WfoTreeBranch key={item.id} item={item} level={0} />
24
25
  ))}
25
26
  </div>
@@ -5,6 +5,7 @@ import { EuiListGroup } from '@elastic/eui';
5
5
  import { TreeContext, TreeContextType } from '../../contexts';
6
6
  import { TreeBlock } from '../../types';
7
7
  import { WfoTreeNode } from './WfoTreeNode';
8
+ import { sortTreeBlockByLabel } from './treeUtils';
8
9
 
9
10
  type WfoTreeBranchProps = {
10
11
  item: TreeBlock;
@@ -21,9 +22,15 @@ export const WfoTreeBranch: FC<WfoTreeBranchProps> = ({ item, level }) => {
21
22
  if (hasChildren) {
22
23
  const newLevel = level + 1;
23
24
 
24
- return item.children.map((child) => (
25
- <WfoTreeBranch key={child.id} item={child} level={newLevel} />
26
- ));
25
+ return [...item.children]
26
+ .sort(sortTreeBlockByLabel)
27
+ .map((child) => (
28
+ <WfoTreeBranch
29
+ key={child.id}
30
+ item={child}
31
+ level={newLevel}
32
+ />
33
+ ));
27
34
  }
28
35
 
29
36
  return null;
@@ -19,3 +19,16 @@ export function getWfoTreeNodeDepth(
19
19
  }
20
20
  }
21
21
  }
22
+
23
+ export const sortTreeBlockByLabel = (
24
+ { label: labelA }: TreeBlock,
25
+ { label: labelB }: TreeBlock,
26
+ ): number => {
27
+ if (labelA < labelB) {
28
+ return -1;
29
+ }
30
+ if (labelA > labelB) {
31
+ return 1;
32
+ }
33
+ return 0;
34
+ };
@@ -26,87 +26,80 @@ import {
26
26
  EuiOverlayMask,
27
27
  } from '@elastic/eui';
28
28
 
29
+ import { ConfirmDialogHandler } from '@/contexts/ConfirmationDialogProvider';
30
+
29
31
  import { confirmationDialogStyling } from './ConfirmationDialogStyling';
30
32
 
31
- interface IProps {
33
+ interface WfoConfirmationDialogProps {
32
34
  isOpen?: boolean;
33
- cancel: (
34
- e:
35
- | React.KeyboardEvent<HTMLDivElement>
36
- | React.MouseEvent<HTMLButtonElement, MouseEvent>
37
- | undefined,
38
- ) => void;
39
- confirm: (
40
- e:
41
- | React.KeyboardEvent<HTMLDivElement>
42
- | React.MouseEvent<HTMLButtonElement, MouseEvent>
43
- | undefined,
44
- ) => void;
45
- question?: string;
46
- leavePage?: boolean;
35
+ onCancel: ConfirmDialogHandler;
36
+ onConfirm: ConfirmDialogHandler;
37
+ question: string;
38
+ subQuestion?: string;
39
+ cancelButtonText?: string;
40
+ confirmButtonText?: string;
47
41
  isError?: boolean;
48
42
  }
49
43
 
50
44
  export default function WfoConfirmationDialog({
51
45
  isOpen = false,
52
- cancel,
53
- confirm,
46
+ onCancel,
47
+ onConfirm,
54
48
  question = '',
55
- leavePage = false,
49
+ subQuestion = '',
50
+ cancelButtonText = '',
51
+ confirmButtonText = '',
56
52
  isError = false,
57
- }: IProps) {
53
+ }: WfoConfirmationDialogProps) {
58
54
  const t = useTranslations('confirmationDialog');
59
- const modalContent = (
60
- <div>
61
- {leavePage ? (
62
- <section
63
- className={`dialog-content ${isError ? ' error' : ''}`}
64
- >
65
- <h2>{question || t('leavePage')}</h2>
66
- {!question && <p>{t('leavePageSub')}</p>}
67
- </section>
68
- ) : (
69
- <section className="dialog-content">
70
- <h2>{question}</h2>
71
- </section>
72
- )}
73
- </div>
74
- );
75
-
76
- let modal;
77
55
 
78
- if (isOpen) {
79
- modal = (
80
- <EuiOverlayMask>
81
- <EuiModal
82
- css={confirmationDialogStyling}
83
- className="confirm-modal"
84
- onClose={leavePage ? confirm : cancel}
85
- initialFocus="[name=popfirst]"
86
- >
87
- <EuiModalHeader>
88
- <EuiModalHeaderTitle>{t('title')}</EuiModalHeaderTitle>
89
- </EuiModalHeader>
56
+ return (
57
+ <div className="confirmation-dialog-overlay">
58
+ {isOpen && (
59
+ <EuiOverlayMask>
60
+ <EuiModal
61
+ css={confirmationDialogStyling}
62
+ className="confirm-modal"
63
+ onClose={onCancel}
64
+ initialFocus="[name=popfirst]"
65
+ >
66
+ <EuiModalHeader>
67
+ <EuiModalHeaderTitle>
68
+ {t('title')}
69
+ </EuiModalHeaderTitle>
70
+ </EuiModalHeader>
90
71
 
91
- <EuiModalBody>{modalContent}</EuiModalBody>
72
+ <EuiModalBody>
73
+ <div>
74
+ <section
75
+ className={`dialog-content ${isError ? ' error' : ''}`}
76
+ >
77
+ <h2>{question}</h2>
78
+ {subQuestion && <p>{subQuestion}</p>}
79
+ </section>
80
+ </div>
81
+ </EuiModalBody>
92
82
 
93
- <EuiModalFooter>
94
- <EuiButton
95
- onClick={cancel}
96
- id="dialog-cancel"
97
- fill={false}
98
- >
99
- {leavePage ? t('leave') : t('cancel')}
100
- </EuiButton>
83
+ <EuiModalFooter>
84
+ <EuiButton
85
+ onClick={onCancel}
86
+ id="dialog-cancel"
87
+ fill={false}
88
+ >
89
+ {cancelButtonText || t('cancel')}
90
+ </EuiButton>
101
91
 
102
- <EuiButton onClick={confirm} fill id="dialog-confirm">
103
- {leavePage ? t('stay') : t('confirm')}
104
- </EuiButton>
105
- </EuiModalFooter>
106
- </EuiModal>
107
- </EuiOverlayMask>
108
- );
109
- }
110
-
111
- return <div className="confirmation-dialog-overlay">{modal}</div>;
92
+ <EuiButton
93
+ onClick={onConfirm}
94
+ fill
95
+ id="dialog-confirm"
96
+ >
97
+ {confirmButtonText || t('confirm')}
98
+ </EuiButton>
99
+ </EuiModalFooter>
100
+ </EuiModal>
101
+ </EuiOverlayMask>
102
+ )}
103
+ </div>
104
+ );
112
105
  }