@axinom/mosaic-ui 0.43.0-rc.9 → 0.43.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 (46) hide show
  1. package/dist/components/Explorer/Explorer.d.ts +2 -0
  2. package/dist/components/Explorer/Explorer.d.ts.map +1 -1
  3. package/dist/components/Explorer/NavigationExplorer/NavigationExplorer.d.ts +1 -1
  4. package/dist/components/Explorer/NavigationExplorer/NavigationExplorer.d.ts.map +1 -1
  5. package/dist/components/Explorer/SelectionExplorer/SelectionExplorer.d.ts +1 -1
  6. package/dist/components/Explorer/SelectionExplorer/SelectionExplorer.d.ts.map +1 -1
  7. package/dist/components/FormStation/FormStation.d.ts +3 -1
  8. package/dist/components/FormStation/FormStation.d.ts.map +1 -1
  9. package/dist/components/FormStation/FormStationHeader/FormStationHeader.d.ts +1 -0
  10. package/dist/components/FormStation/FormStationHeader/FormStationHeader.d.ts.map +1 -1
  11. package/dist/components/FormStation/helpers/useTitle.d.ts +3 -0
  12. package/dist/components/FormStation/helpers/useTitle.d.ts.map +1 -0
  13. package/dist/components/PageHeader/PageHeader.d.ts.map +1 -1
  14. package/dist/components/PageHeader/PageHeader.model.d.ts +2 -0
  15. package/dist/components/PageHeader/PageHeader.model.d.ts.map +1 -1
  16. package/dist/hooks/useTabTitle/useTabTitle.d.ts +2 -0
  17. package/dist/hooks/useTabTitle/useTabTitle.d.ts.map +1 -0
  18. package/dist/index.es.js +4 -4
  19. package/dist/index.es.js.map +1 -1
  20. package/dist/index.js +3 -3
  21. package/dist/index.js.map +1 -1
  22. package/dist/initialize.d.ts +2 -0
  23. package/dist/initialize.d.ts.map +1 -1
  24. package/package.json +3 -3
  25. package/src/components/EmptyStation/EmptyStation.spec.tsx +3 -1
  26. package/src/components/EmptyStation/EmptyStation.stories.tsx +1 -0
  27. package/src/components/Explorer/Explorer.spec.tsx +2 -0
  28. package/src/components/Explorer/Explorer.stories.tsx +4 -0
  29. package/src/components/Explorer/Explorer.tsx +5 -0
  30. package/src/components/Explorer/NavigationExplorer/NavigationExplorer.spec.tsx +2 -0
  31. package/src/components/Explorer/NavigationExplorer/NavigationExplorer.tsx +2 -1
  32. package/src/components/Explorer/SelectionExplorer/SelectionExplorer.spec.tsx +2 -0
  33. package/src/components/Explorer/SelectionExplorer/SelectionExplorer.tsx +6 -1
  34. package/src/components/FormStation/Create/Create.stories.tsx +1 -1
  35. package/src/components/FormStation/FormStation.spec.tsx +2 -1
  36. package/src/components/FormStation/FormStation.stories.tsx +1 -0
  37. package/src/components/FormStation/FormStation.tsx +4 -0
  38. package/src/components/FormStation/FormStationHeader/FormStationHeader.tsx +6 -5
  39. package/src/components/FormStation/helpers/useTitle.spec.ts +52 -0
  40. package/src/components/FormStation/helpers/useTitle.ts +20 -0
  41. package/src/components/PageHeader/PageHeader.model.ts +2 -0
  42. package/src/components/PageHeader/PageHeader.spec.tsx +2 -0
  43. package/src/components/PageHeader/PageHeader.tsx +4 -0
  44. package/src/hooks/useTabTitle/useTabTitle.spec.tsx +84 -0
  45. package/src/hooks/useTabTitle/useTabTitle.tsx +15 -0
  46. package/src/initialize.ts +4 -0
@@ -9,6 +9,7 @@ export declare let addIndicator: AddIndicator | (() => void);
9
9
  export declare let removeIndicator: RemoveIndicator | (() => void);
10
10
  export declare let on: CustomEventEmitter['on'] | (() => void);
11
11
  export declare let setSaveIndicator: (type: SaveIndicatorType) => void;
12
+ export declare let setTitle: (title?: string) => void;
12
13
  /**
13
14
  * Passes the PiralApi methods to the UI library.
14
15
  * @param app {UiConfig} object containing PiralApi methods for use in UI library.
@@ -20,5 +21,6 @@ export interface UiConfig {
20
21
  removeIndicator: RemoveIndicator;
21
22
  on: CustomEventEmitter['on'];
22
23
  setSaveIndicator: (type: SaveIndicatorType) => void;
24
+ setTitle: (title?: string) => void;
23
25
  }
24
26
  //# sourceMappingURL=initialize.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"initialize.d.ts","sourceRoot":"","sources":["../src/initialize.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAE3B,oBAAY,iBAAiB;IAC3B,MAAM,WAAW;IACjB,QAAQ,aAAa;IACrB,KAAK,UAAU;CAChB;AAED,eAAO,IAAI,gBAAgB,EAAE,gBAAgB,GAAG,CAAC,MAAM,IAAI,CAC7B,CAAC;AAE/B,eAAO,IAAI,YAAY,EAAE,YAAY,GAAG,CAAC,MAAM,IAAI,CAA4B,CAAC;AAEhF,eAAO,IAAI,eAAe,EAAE,eAAe,GAAG,CAAC,MAAM,IAAI,CAC5B,CAAC;AAE9B,eAAO,IAAI,EAAE,EAAE,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,CAAkB,CAAC;AAExE,eAAO,IAAI,gBAAgB,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,IAC5B,CAAC;AAE/B;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAQhD;AAED,MAAM,WAAW,QAAQ;IACvB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,YAAY,EAAE,YAAY,CAAC;IAC3B,eAAe,EAAE,eAAe,CAAC;IACjC,EAAE,EAAE,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC7B,gBAAgB,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,IAAI,CAAC;CACrD"}
1
+ {"version":3,"file":"initialize.d.ts","sourceRoot":"","sources":["../src/initialize.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EACjB,MAAM,mBAAmB,CAAC;AAE3B,oBAAY,iBAAiB;IAC3B,MAAM,WAAW;IACjB,QAAQ,aAAa;IACrB,KAAK,UAAU;CAChB;AAED,eAAO,IAAI,gBAAgB,EAAE,gBAAgB,GAAG,CAAC,MAAM,IAAI,CAC7B,CAAC;AAE/B,eAAO,IAAI,YAAY,EAAE,YAAY,GAAG,CAAC,MAAM,IAAI,CAA4B,CAAC;AAEhF,eAAO,IAAI,eAAe,EAAE,eAAe,GAAG,CAAC,MAAM,IAAI,CAC5B,CAAC;AAE9B,eAAO,IAAI,EAAE,EAAE,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,CAAkB,CAAC;AAExE,eAAO,IAAI,gBAAgB,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,IAC5B,CAAC;AAE/B,eAAO,IAAI,QAAQ,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,IAA2B,CAAC;AAErE;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAShD;AAED,MAAM,WAAW,QAAQ;IACvB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,YAAY,EAAE,YAAY,CAAC;IAC3B,eAAe,EAAE,eAAe,CAAC;IACjC,EAAE,EAAE,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC7B,gBAAgB,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,IAAI,CAAC;IACpD,QAAQ,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axinom/mosaic-ui",
3
- "version": "0.43.0-rc.9",
3
+ "version": "0.43.1",
4
4
  "description": "UI components for building Axinom Mosaic applications",
5
5
  "author": "Axinom",
6
6
  "license": "PROPRIETARY",
@@ -32,7 +32,7 @@
32
32
  "build-storybook": "storybook build"
33
33
  },
34
34
  "dependencies": {
35
- "@axinom/mosaic-core": "^0.4.16-rc.9",
35
+ "@axinom/mosaic-core": "^0.4.16",
36
36
  "@faker-js/faker": "^7.4.0",
37
37
  "@popperjs/core": "^2.11.8",
38
38
  "clsx": "^1.1.0",
@@ -105,5 +105,5 @@
105
105
  "publishConfig": {
106
106
  "access": "public"
107
107
  },
108
- "gitHead": "ab964b4541c49ea5b84693de4d2a2c809dbb0f51"
108
+ "gitHead": "6df139c63de76d40be27d5a49ec2f159c15657dd"
109
109
  }
@@ -1,10 +1,12 @@
1
1
  import { mount, shallow } from 'enzyme';
2
2
  import React from 'react';
3
3
  import { MessageBar } from '../MessageBar';
4
- import { StationError } from '../models';
5
4
  import { PageHeader } from '../PageHeader';
5
+ import { StationError } from '../models';
6
6
  import { EmptyStation, EmptyStationProps } from './EmptyStation';
7
7
 
8
+ jest.mock('../../hooks/useTabTitle/useTabTitle');
9
+
8
10
  describe('EmptyStation', () => {
9
11
  const defaultProps: EmptyStationProps = {
10
12
  title: 'test-title',
@@ -13,6 +13,7 @@ const groups = createGroups({
13
13
  'bulkActionsDisabled',
14
14
  'openBulkActionsOnStart',
15
15
  'onBulkActionsToggled',
16
+ 'setTabTitle',
16
17
  ],
17
18
  Content: [
18
19
  'title',
@@ -34,6 +34,8 @@ import {
34
34
  } from './Explorer.model';
35
35
  import { StationMessage } from './useStationMessage';
36
36
 
37
+ jest.mock('../../hooks/useTabTitle/useTabTitle');
38
+
37
39
  interface ExplorerTestData {
38
40
  id: number;
39
41
  title: string;
@@ -54,6 +54,7 @@ const groups = createGroups({
54
54
  'onItemClicked',
55
55
  'openBulkActionsOnStart',
56
56
  'persistExplorerStates',
57
+ 'setTabTitle',
57
58
  ],
58
59
  Styling: [
59
60
  'className',
@@ -251,6 +252,9 @@ initializeUi({
251
252
  on: (event, callback) => {
252
253
  action('on')(event, callback);
253
254
  },
255
+ setTitle: (args) => {
256
+ action('setTitle')(args);
257
+ },
254
258
  });
255
259
 
256
260
  export const ActionErrors: StoryObj<ExplorerStoryType> = {
@@ -124,6 +124,9 @@ export interface ExplorerProps<T extends Data> {
124
124
  /** Sets the default sort order for the Explorer. (default: undefined) */
125
125
  defaultSortOrder?: SortData<T>;
126
126
 
127
+ /** Update the tab title using the 'title' field. (default: true) */
128
+ setTabTitle?: boolean;
129
+
127
130
  /**
128
131
  * When set, this function is used to generate the link that the user should be navigated to for each item.
129
132
  *
@@ -196,6 +199,7 @@ export const Explorer = React.forwardRef(function Explorer<T extends Data>(
196
199
  className = '',
197
200
 
198
201
  defaultSortOrder,
202
+ setTabTitle = true,
199
203
 
200
204
  onItemClicked,
201
205
  onBulkActionsToggled = noop,
@@ -423,6 +427,7 @@ export const Explorer = React.forwardRef(function Explorer<T extends Data>(
423
427
  setIsBulkOpen(isOpen);
424
428
  onBulkActionsToggled(isOpen);
425
429
  }}
430
+ setTabTitle={setTabTitle}
426
431
  />
427
432
  {StationMessage}
428
433
  <Filters<T>
@@ -12,6 +12,8 @@ import { Explorer } from '../Explorer';
12
12
  import { ExplorerDataProvider } from '../Explorer.model';
13
13
  import { NavigationExplorer } from './NavigationExplorer';
14
14
 
15
+ jest.mock('../../../hooks/useTabTitle/useTabTitle');
16
+
15
17
  interface NavExplorerTestData {
16
18
  id: string;
17
19
  desc: string;
@@ -9,7 +9,7 @@ import { ExplorerDataProviderConnection } from '../Explorer.model';
9
9
  export interface NavigationExplorerProps<T extends Data>
10
10
  extends Omit<
11
11
  ExplorerProps<T>,
12
- 'selectionMode' | 'onBulkActionsToggled' | 'onItemClicked'
12
+ 'selectionMode' | 'onBulkActionsToggled' | 'onItemClicked' | 'setTabTitle'
13
13
  > {
14
14
  /**
15
15
  * - If a `LocationDescriptor` is provided, it will be treated as a path to the station to
@@ -54,6 +54,7 @@ export const NavigationExplorer = React.forwardRef(function NavigationExplorer<
54
54
  <Explorer<T>
55
55
  {...rest}
56
56
  ref={ref}
57
+ setTabTitle={true}
57
58
  actions={[
58
59
  ...actions,
59
60
  ...(onCreateAction && typeof onCreateAction !== 'function'
@@ -11,6 +11,8 @@ import { Explorer } from '../Explorer';
11
11
  import { ExplorerDataProvider } from '../Explorer.model';
12
12
  import { SelectionExplorer } from './SelectionExplorer';
13
13
 
14
+ jest.mock('../../../hooks/useTabTitle/useTabTitle');
15
+
14
16
  interface SelectExplorerTestData {
15
17
  id: string;
16
18
  desc: string;
@@ -15,7 +15,11 @@ import {
15
15
  export interface SelectionExplorerProps<T extends Data>
16
16
  extends Omit<
17
17
  ExplorerProps<T>,
18
- 'selectionMode' | 'onItemClicked' | 'onBulkActionsToggled' | 'bulkActions'
18
+ | 'selectionMode'
19
+ | 'onItemClicked'
20
+ | 'onBulkActionsToggled'
21
+ | 'bulkActions'
22
+ | 'setTabTitle'
19
23
  > {
20
24
  /** Whether or not the selection of multiple items is allowed (default: false) */
21
25
  allowBulkSelect?: boolean;
@@ -79,6 +83,7 @@ export const SelectionExplorer = React.forwardRef(function SelectionExplorer<
79
83
  {...rest}
80
84
  ref={ref}
81
85
  modalMode={modalMode}
86
+ setTabTitle={false}
82
87
  bulkActions={[
83
88
  ...(allowBulkSelect
84
89
  ? [
@@ -13,7 +13,7 @@ import {
13
13
  TagsField,
14
14
  } from '../../FormElements';
15
15
  import { FileUploadField } from '../../FormElements/FileUploadControl/FileUploadField';
16
- import { ObjectSchemaDefinition } from '../FormStation';
16
+ import { ObjectSchemaDefinition } from '../FormStation.models';
17
17
  import { Create } from './Create';
18
18
 
19
19
  interface CreateValues {
@@ -10,7 +10,8 @@ import { ActionData, Actions } from '../Actions';
10
10
  import { Action } from '../Actions/Action';
11
11
  import { MessageBar } from '../MessageBar/MessageBar';
12
12
  import { PageHeader, PageHeaderAction } from '../PageHeader';
13
- import { FormStation, ObjectSchemaDefinition } from './FormStation';
13
+ import { FormStation } from './FormStation';
14
+ import { ObjectSchemaDefinition } from './FormStation.models';
14
15
  import { SaveOnNavigate } from './SaveOnNavigate/SaveOnNavigate';
15
16
 
16
17
  jest.mock('../../initialize');
@@ -47,6 +47,7 @@ const groups = createGroups({
47
47
  'alwaysShowActionsPanel',
48
48
  'alwaysSubmitBeforeAction',
49
49
  'edgeToEdgeContent',
50
+ 'setTabTitle',
50
51
  ],
51
52
  Styling: ['actionsWidth', 'className'],
52
53
  });
@@ -56,6 +56,8 @@ export interface FormStationProps<
56
56
  * @example stationMessage={{type: 'info', message: 'Informative message.'}}
57
57
  */
58
58
  stationMessage?: StationMessage;
59
+ /** Update the tab title using the 'titleProperty' or 'defaultTitle' field. (default: true) */
60
+ setTabTitle?: boolean;
59
61
  /**
60
62
  * Called whenever the form needs to be saved.
61
63
  * This method needs to throw an exception in case the saving did not succeed.
@@ -82,6 +84,7 @@ export const FormStation = <TValues extends Data, TSubmitResponse = unknown>({
82
84
  alwaysSubmitBeforeAction = false,
83
85
  stationMessage,
84
86
  className = '',
87
+ setTabTitle = true,
85
88
  }: PropsWithChildren<
86
89
  FormStationProps<TValues, TSubmitResponse>
87
90
  >): JSX.Element => {
@@ -123,6 +126,7 @@ export const FormStation = <TValues extends Data, TSubmitResponse = unknown>({
123
126
  subtitle={subtitle}
124
127
  cancelNavigationUrl={cancelNavigationUrl}
125
128
  className={classes.header}
129
+ setTabTitle={setTabTitle}
126
130
  />
127
131
  <SaveOnNavigate
128
132
  isSubmitting={isFormSubmitting}
@@ -8,6 +8,7 @@ import {
8
8
  PageHeaderActionType,
9
9
  PageHeaderProps,
10
10
  } from '../../PageHeader';
11
+ import { useTitle } from '../helpers/useTitle';
11
12
 
12
13
  /**
13
14
  * Handles showRefresh and cancel buttons based on form states
@@ -17,6 +18,7 @@ export const FormStationHeader: React.FC<
17
18
  titleProperty?: string;
18
19
  defaultTitle?: string;
19
20
  cancelNavigationUrl?: string;
21
+ setTabTitle?: boolean;
20
22
  }
21
23
  > = ({
22
24
  titleProperty,
@@ -24,8 +26,9 @@ export const FormStationHeader: React.FC<
24
26
  subtitle,
25
27
  cancelNavigationUrl,
26
28
  className,
29
+ setTabTitle,
27
30
  }) => {
28
- const { dirty, resetForm, values } = useFormikContext<FormikValues>();
31
+ const { dirty, resetForm } = useFormikContext<FormikValues>();
29
32
 
30
33
  useEffect(() => {
31
34
  // Set the save indicator to dirty depending on the form state
@@ -44,16 +47,14 @@ export const FormStationHeader: React.FC<
44
47
 
45
48
  const history = useHistory();
46
49
 
47
- const title =
48
- titleProperty && values[titleProperty] !== ''
49
- ? values[titleProperty]
50
- : defaultTitle ?? '';
50
+ const title = useTitle(titleProperty, defaultTitle);
51
51
 
52
52
  return (
53
53
  <PageHeader
54
54
  title={title}
55
55
  subtitle={subtitle}
56
56
  className={className}
57
+ setTabTitle={setTabTitle}
57
58
  actions={[
58
59
  ...(dirty === true // add undo action if form as been altered
59
60
  ? [
@@ -0,0 +1,52 @@
1
+ import { useFormikContext } from 'formik';
2
+ import { useTitle } from './useTitle';
3
+
4
+ jest.mock('formik', () => ({
5
+ useFormikContext: jest.fn(),
6
+ }));
7
+
8
+ describe('useTitle', () => {
9
+ afterEach(() => {
10
+ jest.clearAllMocks();
11
+ });
12
+
13
+ it('should return default title when titleProperty is not provided', () => {
14
+ const defaultTitle = 'Default Title';
15
+ (useFormikContext as jest.Mock).mockReturnValueOnce({});
16
+
17
+ const result = useTitle(undefined, defaultTitle);
18
+
19
+ expect(result).toBe(defaultTitle);
20
+ });
21
+
22
+ it('should return title from formik context when titleProperty is provided', () => {
23
+ const titleProperty = 'title';
24
+ const titleValue = 'Form Title';
25
+ (useFormikContext as jest.Mock).mockReturnValueOnce({
26
+ values: { [titleProperty]: titleValue },
27
+ });
28
+
29
+ const result = useTitle(titleProperty);
30
+
31
+ expect(result).toBe(titleValue);
32
+ });
33
+
34
+ it('should return default title when titleProperty is provided but not found in formik context', () => {
35
+ const titleProperty = 'title';
36
+ const defaultTitle = 'Default Title';
37
+ (useFormikContext as jest.Mock).mockReturnValueOnce({ values: {} });
38
+
39
+ const result = useTitle(titleProperty, defaultTitle);
40
+
41
+ expect(result).toBe(defaultTitle);
42
+ });
43
+
44
+ it('should prepend an * when dirty is true', () => {
45
+ const defaultTitle = 'Default Title';
46
+ (useFormikContext as jest.Mock).mockReturnValueOnce({ dirty: true });
47
+
48
+ const result = useTitle(undefined, defaultTitle);
49
+
50
+ expect(result).toBe(`*${defaultTitle}`);
51
+ });
52
+ });
@@ -0,0 +1,20 @@
1
+ import { useFormikContext } from 'formik';
2
+ import { Data } from '../../../types';
3
+
4
+ export const useTitle = <TValues extends Data>(
5
+ titleProperty?: string,
6
+ defaultTitle?: string,
7
+ ): string | undefined => {
8
+ const { values, dirty } = useFormikContext<TValues>();
9
+
10
+ const title: string | undefined =
11
+ titleProperty && values[titleProperty] !== '' && values[titleProperty]
12
+ ? values[titleProperty]
13
+ : defaultTitle ?? '';
14
+
15
+ if (dirty) {
16
+ return `*${title}`;
17
+ }
18
+
19
+ return title;
20
+ };
@@ -20,4 +20,6 @@ export interface PageHeaderProps {
20
20
  onBulkActionsToggled?: (expanded: boolean) => void;
21
21
  /** CSS Class name for additional styles */
22
22
  className?: string;
23
+ /** Update the tab title using the 'title' field. (default: true) */
24
+ setTabTitle?: boolean;
23
25
  }
@@ -6,6 +6,8 @@ import { PageHeaderAction } from './PageHeaderAction/PageHeaderAction';
6
6
  import { PageHeaderActionProps } from './PageHeaderAction/PageHeaderAction.model';
7
7
  import { PageHeaderBulkActions } from './PageHeaderBulkActions/PageHeaderBulkActions';
8
8
 
9
+ jest.mock('../../hooks/useTabTitle/useTabTitle');
10
+
9
11
  const defaultbulkActions: PageHeaderActionProps[] = [
10
12
  {
11
13
  label: 'Bulk Action 1',
@@ -1,6 +1,7 @@
1
1
  import clsx from 'clsx';
2
2
  import React, { useEffect, useState } from 'react';
3
3
  import { noop } from '../../helpers/utils';
4
+ import { useTabTitle } from '../../hooks/useTabTitle/useTabTitle';
4
5
  import { useWindowSize } from '../../hooks/useWindowSize/useWindowSize';
5
6
  import { PageHeaderProps } from './PageHeader.model';
6
7
  import classes from './PageHeader.scss';
@@ -21,6 +22,7 @@ export const PageHeader: React.FC<PageHeaderProps> = ({
21
22
  openBulkActionsOnStart = false,
22
23
  onBulkActionsToggled = noop,
23
24
  className = '',
25
+ setTabTitle = true,
24
26
  }) => {
25
27
  const [containerScrollWidth, setContainerScrollWidth] = useState<number>(0);
26
28
  const [childrenScrollWidth, setChildrenScrollWidth] = useState<number>(0);
@@ -39,6 +41,8 @@ export const PageHeader: React.FC<PageHeaderProps> = ({
39
41
  }
40
42
  };
41
43
 
44
+ useTabTitle(title, setTabTitle);
45
+
42
46
  useEffect(() => {
43
47
  // only perform calculation if there are bulkActions
44
48
  if (bulkActions.length > 0) {
@@ -0,0 +1,84 @@
1
+ import { mount } from 'enzyme';
2
+ import React from 'react';
3
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
4
+ import { useHistory } from 'react-router-dom';
5
+ import { setTitle } from '../../initialize';
6
+ import { useTabTitle } from './useTabTitle';
7
+
8
+ jest.mock('../../initialize');
9
+ jest.mock('react-router-dom');
10
+
11
+ const TestWrapper: React.FC<{ title?: string; setTabTitle: boolean }> = ({
12
+ title,
13
+ setTabTitle,
14
+ }) => {
15
+ useTabTitle(title, setTabTitle);
16
+
17
+ return null;
18
+ };
19
+
20
+ describe('useTabTitle', () => {
21
+ beforeEach(() => {
22
+ jest.clearAllMocks();
23
+ (useHistory as jest.Mock) = jest.fn().mockReturnValue({
24
+ location: {
25
+ pathname: '/test',
26
+ },
27
+ });
28
+ });
29
+
30
+ it('should set the tab title when setTabTitle is true', () => {
31
+ const firstTitle = 'First Title';
32
+ const secondTitle = 'Second Title';
33
+
34
+ const setTabTitle = true;
35
+
36
+ const wrapper = mount(
37
+ <TestWrapper title={firstTitle} setTabTitle={setTabTitle} />,
38
+ );
39
+
40
+ expect(setTitle).toHaveBeenCalledWith(firstTitle);
41
+
42
+ wrapper.setProps({ title: secondTitle });
43
+
44
+ expect(setTitle).toHaveBeenNthCalledWith(2, secondTitle);
45
+ });
46
+
47
+ it('should not set the tab title when setTabTitle is false', () => {
48
+ const title = 'Test Title';
49
+ const setTabTitle = false;
50
+
51
+ mount(<TestWrapper title={title} setTabTitle={setTabTitle} />);
52
+
53
+ expect(setTitle).not.toHaveBeenCalled();
54
+ });
55
+
56
+ it('should not set the tab title when the URL has changed', () => {
57
+ (useHistory as jest.Mock) = jest
58
+ .fn()
59
+ .mockReturnValueOnce({
60
+ location: {
61
+ pathname: '/test',
62
+ },
63
+ })
64
+ .mockReturnValueOnce({
65
+ location: {
66
+ pathname: '/test2',
67
+ },
68
+ });
69
+
70
+ const firstTitle = 'First Title';
71
+ const secondTitle = 'Second Title';
72
+ const setTabTitle = true;
73
+
74
+ const wrapper = mount(
75
+ <TestWrapper title={firstTitle} setTabTitle={setTabTitle} />,
76
+ );
77
+
78
+ expect(setTitle).toHaveBeenCalledWith(firstTitle);
79
+
80
+ wrapper.setProps({ title: secondTitle });
81
+
82
+ expect(setTitle).toHaveBeenCalledTimes(1);
83
+ });
84
+ });
@@ -0,0 +1,15 @@
1
+ import { useState } from 'react';
2
+ import { useHistory } from 'react-router-dom';
3
+ import { setTitle } from '../../initialize';
4
+
5
+ export const useTabTitle = (
6
+ title: string | undefined,
7
+ setTabTitle: boolean | undefined,
8
+ ): void => {
9
+ const { location: currentLocation } = useHistory();
10
+ const [initialLocation] = useState(currentLocation);
11
+
12
+ if (setTabTitle && currentLocation.pathname === initialLocation.pathname) {
13
+ setTitle(title);
14
+ }
15
+ };
package/src/initialize.ts CHANGED
@@ -24,6 +24,8 @@ export let on: CustomEventEmitter['on'] | (() => void) = polyfill('on');
24
24
  export let setSaveIndicator: (type: SaveIndicatorType) => void =
25
25
  polyfill('setSaveIndicator');
26
26
 
27
+ export let setTitle: (title?: string) => void = polyfill('setTitle');
28
+
27
29
  /**
28
30
  * Passes the PiralApi methods to the UI library.
29
31
  * @param app {UiConfig} object containing PiralApi methods for use in UI library.
@@ -35,6 +37,7 @@ export function initializeUi(app: UiConfig): void {
35
37
  removeIndicator = polyfill('removeIndicator'),
36
38
  on = polyfill('on'),
37
39
  setSaveIndicator = polyfill('setSaveIndicator'),
40
+ setTitle = polyfill('setTitle'),
38
41
  } = app);
39
42
  }
40
43
 
@@ -44,6 +47,7 @@ export interface UiConfig {
44
47
  removeIndicator: RemoveIndicator;
45
48
  on: CustomEventEmitter['on'];
46
49
  setSaveIndicator: (type: SaveIndicatorType) => void;
50
+ setTitle: (title?: string) => void;
47
51
  }
48
52
 
49
53
  function polyfill(methodName: string): () => void {