@patternfly/chatbot 2.2.0 → 6.3.0-prerelease.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 (96) hide show
  1. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +4 -0
  2. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +7 -1
  3. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +23 -0
  4. package/dist/cjs/Message/Message.d.ts +17 -1
  5. package/dist/cjs/Message/Message.js +53 -34
  6. package/dist/cjs/Message/Message.test.js +52 -0
  7. package/dist/cjs/Message/MessageInput.d.ts +18 -0
  8. package/dist/cjs/Message/MessageInput.js +34 -0
  9. package/dist/cjs/MessageBar/MicrophoneButton.js +1 -1
  10. package/dist/cjs/MessageBox/MessageBox.js +5 -5
  11. package/dist/cjs/SourcesCard/SourcesCard.d.ts +7 -1
  12. package/dist/cjs/SourcesCard/SourcesCard.js +16 -10
  13. package/dist/cjs/SourcesCard/SourcesCard.test.js +25 -15
  14. package/dist/cjs/tracking/console_tracking_provider.d.ts +4 -5
  15. package/dist/cjs/tracking/console_tracking_provider.js +22 -15
  16. package/dist/cjs/tracking/posthog_tracking_provider.d.ts +2 -2
  17. package/dist/cjs/tracking/posthog_tracking_provider.js +21 -12
  18. package/dist/cjs/tracking/segment_tracking_provider.d.ts +2 -2
  19. package/dist/cjs/tracking/segment_tracking_provider.js +21 -12
  20. package/dist/cjs/tracking/trackingProviderProxy.d.ts +1 -1
  21. package/dist/cjs/tracking/trackingProviderProxy.js +2 -2
  22. package/dist/cjs/tracking/tracking_api.d.ts +1 -1
  23. package/dist/cjs/tracking/tracking_registry.js +46 -12
  24. package/dist/cjs/tracking/tracking_spi.d.ts +15 -5
  25. package/dist/cjs/tracking/tracking_spi.js +9 -0
  26. package/dist/cjs/tracking/umami_tracking_provider.d.ts +6 -2
  27. package/dist/cjs/tracking/umami_tracking_provider.js +66 -22
  28. package/dist/css/main.css +7 -7
  29. package/dist/css/main.css.map +1 -1
  30. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +4 -0
  31. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +7 -1
  32. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +23 -0
  33. package/dist/esm/Message/Message.d.ts +17 -1
  34. package/dist/esm/Message/Message.js +53 -34
  35. package/dist/esm/Message/Message.test.js +52 -0
  36. package/dist/esm/Message/MessageInput.d.ts +18 -0
  37. package/dist/esm/Message/MessageInput.js +29 -0
  38. package/dist/esm/MessageBar/MicrophoneButton.js +1 -1
  39. package/dist/esm/MessageBox/MessageBox.js +5 -5
  40. package/dist/esm/SourcesCard/SourcesCard.d.ts +7 -1
  41. package/dist/esm/SourcesCard/SourcesCard.js +17 -11
  42. package/dist/esm/SourcesCard/SourcesCard.test.js +25 -15
  43. package/dist/esm/tracking/console_tracking_provider.d.ts +4 -5
  44. package/dist/esm/tracking/console_tracking_provider.js +22 -15
  45. package/dist/esm/tracking/posthog_tracking_provider.d.ts +2 -2
  46. package/dist/esm/tracking/posthog_tracking_provider.js +21 -12
  47. package/dist/esm/tracking/segment_tracking_provider.d.ts +2 -2
  48. package/dist/esm/tracking/segment_tracking_provider.js +21 -12
  49. package/dist/esm/tracking/trackingProviderProxy.d.ts +1 -1
  50. package/dist/esm/tracking/trackingProviderProxy.js +2 -2
  51. package/dist/esm/tracking/tracking_api.d.ts +1 -1
  52. package/dist/esm/tracking/tracking_registry.js +46 -12
  53. package/dist/esm/tracking/tracking_spi.d.ts +15 -5
  54. package/dist/esm/tracking/tracking_spi.js +8 -1
  55. package/dist/esm/tracking/umami_tracking_provider.d.ts +6 -2
  56. package/dist/esm/tracking/umami_tracking_provider.js +66 -22
  57. package/dist/tsconfig.tsbuildinfo +1 -1
  58. package/package.json +2 -2
  59. package/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md +18 -14
  60. package/patternfly-docs/content/extensions/chatbot/examples/Messages/BotMessage.tsx +74 -104
  61. package/patternfly-docs/content/extensions/chatbot/examples/Messages/FileDetailsLabel.tsx +48 -37
  62. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithQuickResponses.tsx +10 -0
  63. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithSources.tsx +51 -14
  64. package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +3 -1
  65. package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx +80 -104
  66. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawer.tsx +35 -2
  67. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerResizable.tsx +13 -2
  68. package/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md +1 -1
  69. package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.tsx +6 -3
  70. package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotAttachment.tsx +2 -0
  71. package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotAttachmentMenu.tsx +2 -0
  72. package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotInDrawer.tsx +2 -0
  73. package/patternfly-docs/content/extensions/chatbot/examples/demos/EmbeddedChatbot.tsx +2 -0
  74. package/patternfly-docs/content/extensions/chatbot/examples/demos/EmbeddedComparisonChatbot.tsx +62 -57
  75. package/patternfly-docs/content/extensions/chatbot/examples/demos/Feedback.tsx +2 -0
  76. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx +53 -0
  77. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx +14 -0
  78. package/src/FileDropZone/__snapshots__/FileDropZone.test.tsx.snap +1 -0
  79. package/src/Message/Message.scss +4 -0
  80. package/src/Message/Message.test.tsx +62 -0
  81. package/src/Message/Message.tsx +111 -53
  82. package/src/Message/MessageInput.tsx +59 -0
  83. package/src/MessageBar/MicrophoneButton.tsx +1 -1
  84. package/src/MessageBox/MessageBox.tsx +5 -5
  85. package/src/SourcesCard/SourcesCard.scss +3 -7
  86. package/src/SourcesCard/SourcesCard.test.tsx +30 -22
  87. package/src/SourcesCard/SourcesCard.tsx +54 -12
  88. package/src/tracking/console_tracking_provider.ts +21 -17
  89. package/src/tracking/posthog_tracking_provider.ts +20 -13
  90. package/src/tracking/segment_tracking_provider.ts +20 -13
  91. package/src/tracking/trackingProviderProxy.ts +2 -2
  92. package/src/tracking/tracking_api.ts +1 -1
  93. package/src/tracking/tracking_registry.ts +46 -13
  94. package/src/tracking/tracking_spi.ts +18 -7
  95. package/src/tracking/umami_tracking_provider.ts +76 -20
  96. package/src/SourcesCard/__snapshots__/SourcesCard.test.tsx.snap +0 -34
@@ -12,22 +12,31 @@ import {
12
12
  CardFooter,
13
13
  CardProps,
14
14
  CardTitle,
15
+ ExpandableSection,
16
+ ExpandableSectionVariant,
15
17
  Icon,
16
18
  pluralize,
17
19
  Truncate
18
20
  } from '@patternfly/react-core';
21
+ import { ExternalLinkSquareAltIcon } from '@patternfly/react-icons';
19
22
 
20
23
  export interface SourcesCardProps extends CardProps {
21
24
  /** Additional classes for the pagination navigation container. */
22
25
  className?: string;
23
26
  /** Flag indicating if the pagination is disabled. */
24
27
  isDisabled?: boolean;
25
- /** Label for the English word "of". */
28
+ /** @deprecated ofWord has been deprecated. Label for the English word "of." */
26
29
  ofWord?: string;
27
30
  /** Accessible label for the pagination component. */
28
31
  paginationAriaLabel?: string;
29
32
  /** Content rendered inside the paginated card */
30
- sources: { title?: string; link: string; body?: React.ReactNode | string }[];
33
+ sources: {
34
+ title?: string;
35
+ link: string;
36
+ body?: React.ReactNode | string;
37
+ isExternal?: boolean;
38
+ hasShowMore?: boolean;
39
+ }[];
31
40
  /** Label for the English word "source" */
32
41
  sourceWord?: string;
33
42
  /** Plural for sourceWord */
@@ -42,12 +51,15 @@ export interface SourcesCardProps extends CardProps {
42
51
  onPreviousClick?: (event: React.SyntheticEvent<HTMLButtonElement>, page: number) => void;
43
52
  /** Function called when page is changed. */
44
53
  onSetPage?: (event: React.MouseEvent | React.KeyboardEvent | MouseEvent, newPage: number) => void;
54
+ /** Label for English words "show more" */
55
+ showMoreWords?: string;
56
+ /** Label for English words "show less" */
57
+ showLessWords?: string;
45
58
  }
46
59
 
47
60
  const SourcesCard: React.FunctionComponent<SourcesCardProps> = ({
48
61
  className,
49
62
  isDisabled,
50
- ofWord = 'of',
51
63
  paginationAriaLabel = 'Pagination',
52
64
  sources,
53
65
  sourceWord = 'source',
@@ -57,9 +69,16 @@ const SourcesCard: React.FunctionComponent<SourcesCardProps> = ({
57
69
  onNextClick,
58
70
  onPreviousClick,
59
71
  onSetPage,
72
+ showMoreWords = 'show more',
73
+ showLessWords = 'show less',
60
74
  ...props
61
75
  }: SourcesCardProps) => {
62
76
  const [page, setPage] = React.useState(1);
77
+ const [isExpanded, setIsExpanded] = React.useState(false);
78
+
79
+ const onToggle = (_event: React.MouseEvent, isExpanded: boolean) => {
80
+ setIsExpanded(isExpanded);
81
+ };
63
82
 
64
83
  const handleNewPage = (_evt: React.MouseEvent | React.KeyboardEvent | MouseEvent, newPage: number) => {
65
84
  setPage(newPage);
@@ -78,13 +97,37 @@ const SourcesCard: React.FunctionComponent<SourcesCardProps> = ({
78
97
  <span>{pluralize(sources.length, sourceWord, sourceWordPlural)}</span>
79
98
  <Card className="pf-chatbot__sources-card" {...props}>
80
99
  <CardTitle className="pf-chatbot__sources-card-title">
81
- <a href={sources[page - 1].link}>{renderTitle(sources[page - 1].title)}</a>
100
+ <Button
101
+ component="a"
102
+ variant={ButtonVariant.link}
103
+ href={sources[page - 1].link}
104
+ icon={sources[page - 1].isExternal ? <ExternalLinkSquareAltIcon /> : undefined}
105
+ iconPosition="end"
106
+ isInline
107
+ rel={sources[page - 1].isExternal ? 'noreferrer' : undefined}
108
+ target={sources[page - 1].isExternal ? '_blank' : undefined}
109
+ >
110
+ {renderTitle(sources[page - 1].title)}
111
+ </Button>
82
112
  </CardTitle>
83
113
  {sources[page - 1].body && (
84
- <CardBody
85
- className={`pf-chatbot__sources-card-body ${sources.length === 1 && 'pf-chatbot__sources-card-no-footer'}`}
86
- >
87
- {sources[page - 1].body}
114
+ <CardBody className={`pf-chatbot__sources-card-body`}>
115
+ {sources[page - 1].hasShowMore ? (
116
+ // prevents extra VO announcements of button text - parent Message has aria-live
117
+ <div aria-live="off">
118
+ <ExpandableSection
119
+ variant={ExpandableSectionVariant.truncate}
120
+ toggleText={isExpanded ? showLessWords : showMoreWords}
121
+ onToggle={onToggle}
122
+ isExpanded={isExpanded}
123
+ truncateMaxLines={2}
124
+ >
125
+ {sources[page - 1].body}
126
+ </ExpandableSection>
127
+ </div>
128
+ ) : (
129
+ <div className="pf-chatbot__sources-card-body-text">{sources[page - 1].body}</div>
130
+ )}
88
131
  </CardBody>
89
132
  )}
90
133
  {sources.length > 1 && (
@@ -117,6 +160,9 @@ const SourcesCard: React.FunctionComponent<SourcesCardProps> = ({
117
160
  </svg>
118
161
  </Icon>
119
162
  </Button>
163
+ <span aria-hidden="true">
164
+ {page}/{sources.length}
165
+ </span>
120
166
  <Button
121
167
  variant={ButtonVariant.plain}
122
168
  isDisabled={isDisabled || page === sources.length}
@@ -144,10 +190,6 @@ const SourcesCard: React.FunctionComponent<SourcesCardProps> = ({
144
190
  </Icon>
145
191
  </Button>
146
192
  </nav>
147
-
148
- <span aria-hidden="true">
149
- {page} {ofWord} {sources.length}
150
- </span>
151
193
  </div>
152
194
  </CardFooter>
153
195
  )}
@@ -1,30 +1,34 @@
1
- import { TrackingSpi } from './tracking_spi';
1
+ import { InitProps, TrackingSpi } from './tracking_spi';
2
2
  import { TrackingApi, TrackingEventProperties } from './tracking_api';
3
3
 
4
4
  export class ConsoleTrackingProvider implements TrackingSpi, TrackingApi {
5
+ private verbose = false;
5
6
  trackPageView(url: string | undefined) {
6
- // eslint-disable-next-line no-console
7
- console.log('ConsoleProvider pageView', url);
7
+ if (this.verbose) {
8
+ // eslint-disable-next-line no-console
9
+ console.log('ConsoleProvider pageView ', url);
10
+ }
8
11
  }
9
- // eslint-disable-next-line @typescript-eslint/no-empty-function
10
- registerProvider(): void {}
11
12
 
12
- initialize(): void {
13
- // eslint-disable-next-line no-console
14
- console.log('ConsoleProvider initialize');
13
+ initialize(props: InitProps): void {
14
+ this.verbose = props.verbose;
15
+ if (this.verbose) {
16
+ // eslint-disable-next-line no-console
17
+ console.log('ConsoleProvider initialize');
18
+ }
15
19
  }
16
20
 
17
- identify(userID: string): void {
18
- // eslint-disable-next-line no-console
19
- console.log('ConsoleProvider identify', userID);
21
+ identify(userID: string, userProperties: TrackingEventProperties = {}): void {
22
+ if (this.verbose) {
23
+ // eslint-disable-next-line no-console
24
+ console.log('ConsoleProvider identify ', userID, userProperties);
25
+ }
20
26
  }
21
27
 
22
28
  trackSingleItem(item: string, properties?: TrackingEventProperties): void {
23
- // eslint-disable-next-line no-console
24
- console.log('ConsoleProvider: ' + item, properties);
25
- }
26
-
27
- getKey(): string {
28
- return 'console';
29
+ if (this.verbose) {
30
+ // eslint-disable-next-line no-console
31
+ console.log('ConsoleProvider: ' + item, properties);
32
+ }
29
33
  }
30
34
  }
@@ -4,13 +4,14 @@ import { TrackingApi, TrackingEventProperties } from './tracking_api';
4
4
  import { InitProps, TrackingSpi } from './tracking_spi';
5
5
 
6
6
  export class PosthogTrackingProvider implements TrackingSpi, TrackingApi {
7
- getKey(): string {
8
- return 'posthogKey';
9
- }
7
+ private verbose = false;
10
8
 
11
9
  initialize(props: InitProps): void {
12
- // eslint-disable-next-line no-console
13
- console.log('PosthogProvider initialize');
10
+ this.verbose = props.verbose;
11
+ if (this.verbose) {
12
+ // eslint-disable-next-line no-console
13
+ console.log('PosthogProvider initialize');
14
+ }
14
15
  const posthogKey = props.posthogKey as string;
15
16
 
16
17
  posthog.init(posthogKey, {
@@ -21,22 +22,28 @@ export class PosthogTrackingProvider implements TrackingSpi, TrackingApi {
21
22
  });
22
23
  }
23
24
 
24
- identify(userID: string): void {
25
- // eslint-disable-next-line no-console
26
- console.log('PosthogProvider userID: ' + userID);
27
- posthog.identify(userID);
25
+ identify(userID: string, userProperties: TrackingEventProperties = {}): void {
26
+ if (this.verbose) {
27
+ // eslint-disable-next-line no-console
28
+ console.log('PosthogProvider userID: ' + userID);
29
+ }
30
+ posthog.identify(userID, userProperties);
28
31
  }
29
32
 
30
33
  trackPageView(url: string | undefined): void {
31
- // eslint-disable-next-line no-console
32
- console.log('PostHogProvider url', url);
34
+ if (this.verbose) {
35
+ // eslint-disable-next-line no-console
36
+ console.log('PostHogProvider url ', url);
37
+ }
33
38
  // TODO posthog seems to record that automatically.
34
39
  // How to not clash with this here? Just leave as no-op?
35
40
  }
36
41
 
37
42
  trackSingleItem(item: string, properties?: TrackingEventProperties): void {
38
- // eslint-disable-next-line no-console
39
- console.log('PosthogProvider: trackSingleItem' + item, properties);
43
+ if (this.verbose) {
44
+ // eslint-disable-next-line no-console
45
+ console.log('PosthogProvider: trackSingleItem ' + item, properties);
46
+ }
40
47
  posthog.capture(item, { properties });
41
48
  }
42
49
  }
@@ -5,13 +5,14 @@ import { InitProps, TrackingSpi } from './tracking_spi';
5
5
 
6
6
  export class SegmentTrackingProvider implements TrackingSpi, TrackingApi {
7
7
  private analytics: AnalyticsBrowser | undefined;
8
- getKey(): string {
9
- return 'segmentKey';
10
- }
8
+ private verbose = false;
11
9
 
12
10
  initialize(props: InitProps): void {
13
- // eslint-disable-next-line no-console
14
- console.log('SegmentProvider initialize');
11
+ this.verbose = props.verbose;
12
+ if (this.verbose) {
13
+ // eslint-disable-next-line no-console
14
+ console.log('SegmentProvider initialize');
15
+ }
15
16
  const segmentKey = props.segmentKey as string;
16
17
 
17
18
  // We need to create an object here, as ts lint is unhappy otherwise
@@ -32,17 +33,21 @@ export class SegmentTrackingProvider implements TrackingSpi, TrackingApi {
32
33
  );
33
34
  }
34
35
 
35
- identify(userID: string): void {
36
- // eslint-disable-next-line no-console
37
- console.log('SegmentProvider userID: ' + userID);
36
+ identify(userID: string, userProperties: TrackingEventProperties = {}): void {
37
+ if (this.verbose) {
38
+ // eslint-disable-next-line no-console
39
+ console.log('SegmentProvider userID: ' + userID);
40
+ }
38
41
  if (this.analytics) {
39
- this.analytics.identify(userID);
42
+ this.analytics.identify(userID, userProperties);
40
43
  }
41
44
  }
42
45
 
43
46
  trackPageView(url: string | undefined): void {
44
- // eslint-disable-next-line no-console
45
- console.log('SegmentProvider url', url);
47
+ if (this.verbose) {
48
+ // eslint-disable-next-line no-console
49
+ console.log('SegmentProvider url ', url);
50
+ }
46
51
  if (this.analytics) {
47
52
  if (url) {
48
53
  this.analytics.page(url);
@@ -53,8 +58,10 @@ export class SegmentTrackingProvider implements TrackingSpi, TrackingApi {
53
58
  }
54
59
 
55
60
  trackSingleItem(item: string, properties?: TrackingEventProperties): void {
56
- // eslint-disable-next-line no-console
57
- console.log('SegmentProvider: trackSingleItem' + item, properties);
61
+ if (this.verbose) {
62
+ // eslint-disable-next-line no-console
63
+ console.log('SegmentProvider: trackSingleItem ' + item, properties);
64
+ }
58
65
  if (this.analytics) {
59
66
  this.analytics.track(item, { properties });
60
67
  }
@@ -6,9 +6,9 @@ class TrackingProviderProxy implements TrackingApi {
6
6
  this.providers = providers;
7
7
  }
8
8
 
9
- identify(userID: string): void {
9
+ identify(userID: string, userProperties: TrackingEventProperties = {}): void {
10
10
  for (const provider of this.providers) {
11
- provider.identify(userID);
11
+ provider.identify(userID, userProperties);
12
12
  }
13
13
  }
14
14
 
@@ -3,7 +3,7 @@ export interface TrackingEventProperties {
3
3
  }
4
4
 
5
5
  export interface TrackingApi {
6
- identify: (userID: string) => void;
6
+ identify: (userID: string, userProperties: TrackingEventProperties) => void;
7
7
 
8
8
  trackPageView: (url: string | undefined) => void;
9
9
 
@@ -1,4 +1,4 @@
1
- import { InitProps, TrackingSpi } from './tracking_spi';
1
+ import { InitProps, Providers, TrackingSpi } from './tracking_spi';
2
2
  import { TrackingApi } from './tracking_api';
3
3
  import TrackingProviderProxy from './trackingProviderProxy';
4
4
  import { ConsoleTrackingProvider } from './console_tracking_provider';
@@ -8,26 +8,59 @@ import { UmamiTrackingProvider } from './umami_tracking_provider';
8
8
 
9
9
  export const getTrackingProviders = (initProps: InitProps): TrackingApi => {
10
10
  const providers: TrackingSpi[] = [];
11
- providers.push(new SegmentTrackingProvider());
12
- providers.push(new PosthogTrackingProvider());
13
- providers.push(new UmamiTrackingProvider());
14
11
 
15
- // TODO dynamically find and register providers
12
+ if (initProps.activeProviders) {
13
+ let tmpProps: string[] = initProps.activeProviders;
14
+
15
+ // Theoretically we get an array of provider names, but it could also be a CSV string...
16
+ if (!Array.isArray(initProps.activeProviders)) {
17
+ const tmpString = initProps.activeProviders as string;
18
+ if (tmpString && tmpString.indexOf(',') !== -1) {
19
+ tmpProps = tmpString.split(',');
20
+ } else {
21
+ tmpProps = [tmpString];
22
+ }
23
+ }
24
+
25
+ tmpProps.forEach((provider) => {
26
+ switch (Providers[provider]) {
27
+ case Providers.Segment:
28
+ providers.push(new SegmentTrackingProvider());
29
+ break;
30
+ case Providers.Umami:
31
+ providers.push(new UmamiTrackingProvider());
32
+ break;
33
+ case Providers.Posthog:
34
+ providers.push(new PosthogTrackingProvider());
35
+ break;
36
+ case Providers.Console:
37
+ providers.push(new ConsoleTrackingProvider());
38
+ break;
39
+ case Providers.None: // Do nothing, just a placeholder
40
+ break;
41
+ default:
42
+ if (providers.length > 1) {
43
+ if (initProps.verbose) {
44
+ // eslint-disable-next-line no-console
45
+ console.error("Unknown provider '" + provider);
46
+ }
47
+ }
48
+ break;
49
+ }
50
+ });
51
+ }
16
52
 
17
53
  // Initialize them
18
- const enabledProviders: TrackingSpi[] = [];
19
54
  for (const provider of providers) {
20
- const key = provider.getKey();
21
- if (Object.keys(initProps).indexOf(key) > -1) {
55
+ try {
22
56
  provider.initialize(initProps);
23
- enabledProviders.push(provider);
57
+ } catch (e) {
58
+ // eslint-disable-next-line no-console
59
+ console.error(e);
24
60
  }
25
61
  }
26
- // Add the console provider
27
- const consoleTrackingProvider = new ConsoleTrackingProvider();
28
- enabledProviders.push(consoleTrackingProvider); // TODO noop- provider?
29
62
 
30
- return new TrackingProviderProxy(enabledProviders);
63
+ return new TrackingProviderProxy(providers);
31
64
  };
32
65
 
33
66
  export default getTrackingProviders;
@@ -1,14 +1,25 @@
1
- import { TrackingApi, TrackingEventProperties } from './tracking_api';
1
+ import { TrackingApi } from './tracking_api';
2
2
 
3
- export interface InitProps {
4
- [key: string]: string | number | boolean;
3
+ export enum Providers {
4
+ None,
5
+ Segment,
6
+ Umami,
7
+ Posthog,
8
+ Console
9
+ }
10
+
11
+ export type ProviderAsString = keyof typeof Providers;
12
+
13
+ export interface BaseProps {
14
+ verbose: boolean;
15
+ activeProviders: [ProviderAsString];
5
16
  }
6
17
 
18
+ export type InitProps = {
19
+ [key: string]: string | number | boolean;
20
+ } & BaseProps;
21
+
7
22
  export interface TrackingSpi extends TrackingApi {
8
- // Return a key in InitProps to check if the provided should be enabled
9
- getKey: () => string;
10
23
  // Initialize the provider
11
24
  initialize: (props: InitProps) => void;
12
- // Track a single item
13
- trackSingleItem: (item: string, properties?: TrackingEventProperties) => void;
14
25
  }
@@ -8,15 +8,25 @@ declare global {
8
8
  }
9
9
  }
10
10
 
11
+ // Items in a queue.
12
+ // We need to queue up requests until the script is fully loaded
13
+ interface queueT {
14
+ what: 'i' | 't' | 'p'; // identify, track, pageview
15
+ name: string;
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ payload?: any;
18
+ }
19
+
11
20
  export class UmamiTrackingProvider implements TrackingSpi, TrackingApi {
12
- getKey(): string {
13
- return 'umamiKey';
14
- }
21
+ private verbose = false;
22
+ private websiteId: string | undefined;
23
+ private queue: queueT[] = [];
15
24
 
16
25
  initialize(props: InitProps): void {
17
- // eslint-disable-next-line no-console
18
- console.log('UmamiProvider initialize');
19
- const umamiKey = props.umamiKey as string;
26
+ this.verbose = props.verbose;
27
+ this.log('UmamiProvider initialize');
28
+
29
+ this.websiteId = props.umamiKey as string;
20
30
  const hostUrl = props.umamiHostUrl as string;
21
31
 
22
32
  const script = document.createElement('script');
@@ -25,30 +35,76 @@ export class UmamiTrackingProvider implements TrackingSpi, TrackingApi {
25
35
  script.defer = true;
26
36
 
27
37
  // Configure Umami properties
28
- script.setAttribute('data-website-id', umamiKey);
29
- script.setAttribute('data-domains', 'localhost'); // TODO ?
38
+ script.setAttribute('data-website-id', this.websiteId);
39
+ script.setAttribute('data-host-url', hostUrl);
30
40
  script.setAttribute('data-auto-track', 'false');
31
- script.setAttribute('data-host-url', hostUrl); // TODO ?
32
- script.setAttribute('data-exclude-search', 'false'); // TODO ?
41
+ script.setAttribute('data-exclude-search', 'false');
42
+
43
+ // Now get from config, which may override some of the above.
44
+ const UMAMI_PREFIX = 'umami-';
45
+ for (const prop in props) {
46
+ if (prop.startsWith(UMAMI_PREFIX)) {
47
+ const att = 'data-' + prop.substring(UMAMI_PREFIX.length);
48
+ const val = props[prop];
49
+ script.setAttribute(att, String(val));
50
+ }
51
+ }
52
+ script.onload = () => {
53
+ this.log('UmamiProvider script loaded');
54
+ this.flushQueue();
55
+ };
33
56
 
34
57
  document.body.appendChild(script);
35
58
  }
36
59
 
37
- identify(userID: string): void {
38
- // eslint-disable-next-line no-console
39
- console.log('UmamiProvider userID: ' + userID);
40
- window.umami?.identify({ userID });
60
+ identify(userID: string, userProperties: TrackingEventProperties = {}): void {
61
+ this.log('UmamiProvider userID: ' + userID + ' => ' + JSON.stringify(userProperties));
62
+ if (window.umami) {
63
+ window.umami.identify({ userID, userProperties });
64
+ } else {
65
+ this.queue.push({ what: 'i', name: userID, payload: userProperties });
66
+ }
41
67
  }
42
68
 
43
69
  trackPageView(url: string | undefined): void {
44
- // eslint-disable-next-line no-console
45
- console.log('UmamiProvider url', url);
46
- window.umami?.track({ url });
70
+ this.log('UmamiProvider url ' + url);
71
+ if (window.umami) {
72
+ window.umami.track({ url, website: this.websiteId });
73
+ } else {
74
+ this.queue.push({ what: 'p', name: String(url) });
75
+ }
47
76
  }
48
77
 
49
78
  trackSingleItem(item: string, properties?: TrackingEventProperties): void {
50
- // eslint-disable-next-line no-console
51
- console.log('UmamiProvider: trackSingleItem' + item, properties);
52
- window.umami?.track(item, properties);
79
+ this.log('UmamiProvider: trackSingleItem ' + item + JSON.stringify(properties));
80
+ if (window.umami) {
81
+ window.umami.track(item, properties);
82
+ } else {
83
+ this.queue.push({ what: 't', name: item, payload: properties });
84
+ }
85
+ }
86
+
87
+ flushQueue(): void {
88
+ for (const item of this.queue) {
89
+ this.log('Queue flush ' + JSON.stringify(item));
90
+ switch (item.what) {
91
+ case 'i':
92
+ this.identify(item.name, item.payload);
93
+ break;
94
+ case 't':
95
+ this.trackSingleItem(item.name, item.payload);
96
+ break;
97
+ case 'p':
98
+ this.trackPageView(item.name);
99
+ break;
100
+ }
101
+ }
102
+ }
103
+
104
+ log(msg: string): void {
105
+ if (this.verbose) {
106
+ // eslint-disable-next-line no-console
107
+ console.debug('UmamiProvider: ', msg);
108
+ }
53
109
  }
54
110
  }
@@ -1,34 +0,0 @@
1
- // Jest Snapshot v1, https://goo.gl/fbAQLP
2
-
3
- exports[`SourcesCard should render card 1`] = `
4
- <div>
5
- <div
6
- class="pf-chatbot__source"
7
- >
8
- <span>
9
- 1 source
10
- </span>
11
- <div
12
- class="pf-v6-c-card pf-chatbot__sources-card"
13
- data-ouia-component-id="OUIA-Generated-Card-1"
14
- data-ouia-component-type="PF6/Card"
15
- data-ouia-safe="true"
16
- id=""
17
- >
18
- <div
19
- class="pf-v6-c-card__title"
20
- >
21
- <div
22
- class="pf-v6-c-card__title-text pf-chatbot__sources-card-title"
23
- >
24
- <a
25
- href=""
26
- >
27
- Source 1
28
- </a>
29
- </div>
30
- </div>
31
- </div>
32
- </div>
33
- </div>
34
- `;