@atlaskit/link-datasource 0.31.2 → 0.32.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 (94) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cjs/analytics/constants.js +8 -0
  3. package/dist/cjs/analytics/generated/analytics.types.js +5 -0
  4. package/dist/cjs/analytics/generated/create-event-payload.js +46 -0
  5. package/dist/cjs/analytics/generated/use-analytics-events.js +32 -0
  6. package/dist/cjs/analytics/index.js +18 -0
  7. package/dist/cjs/ui/common/error-state/access-required.js +12 -3
  8. package/dist/cjs/ui/common/error-state/loading-error.js +20 -11
  9. package/dist/cjs/ui/common/error-state/modal-loading-error.js +21 -12
  10. package/dist/cjs/ui/common/error-state/no-results.js +48 -41
  11. package/dist/cjs/ui/datasource-table-view/datasourceTableView.js +7 -19
  12. package/dist/cjs/ui/issue-like-table/column-picker/index.js +2 -4
  13. package/dist/cjs/ui/issue-like-table/drag-column-preview.js +37 -0
  14. package/dist/cjs/ui/issue-like-table/draggable-table-heading.js +60 -25
  15. package/dist/cjs/ui/issue-like-table/empty-state/index.js +6 -1
  16. package/dist/cjs/ui/issue-like-table/index.js +102 -71
  17. package/dist/cjs/ui/issue-like-table/styled.js +2 -1
  18. package/dist/cjs/ui/jira-issues-modal/jira-search-container/index.js +8 -0
  19. package/dist/cjs/ui/jira-issues-modal/modal/index.js +43 -15
  20. package/dist/cjs/ui/table-footer/index.js +10 -7
  21. package/dist/cjs/version.json +1 -1
  22. package/dist/es2019/analytics/constants.js +1 -0
  23. package/dist/es2019/analytics/generated/analytics.types.js +1 -0
  24. package/dist/es2019/analytics/generated/create-event-payload.js +28 -0
  25. package/dist/es2019/analytics/generated/use-analytics-events.js +24 -0
  26. package/dist/es2019/analytics/index.js +3 -0
  27. package/dist/es2019/ui/common/error-state/access-required.js +10 -0
  28. package/dist/es2019/ui/common/error-state/loading-error.js +10 -0
  29. package/dist/es2019/ui/common/error-state/modal-loading-error.js +10 -0
  30. package/dist/es2019/ui/common/error-state/no-results.js +8 -0
  31. package/dist/es2019/ui/datasource-table-view/datasourceTableView.js +8 -20
  32. package/dist/es2019/ui/issue-like-table/column-picker/index.js +1 -3
  33. package/dist/es2019/ui/issue-like-table/drag-column-preview.js +46 -0
  34. package/dist/es2019/ui/issue-like-table/draggable-table-heading.js +53 -18
  35. package/dist/es2019/ui/issue-like-table/empty-state/index.js +6 -1
  36. package/dist/es2019/ui/issue-like-table/index.js +86 -43
  37. package/dist/es2019/ui/issue-like-table/styled.js +18 -0
  38. package/dist/es2019/ui/jira-issues-modal/jira-search-container/index.js +9 -0
  39. package/dist/es2019/ui/jira-issues-modal/modal/index.js +43 -13
  40. package/dist/es2019/ui/table-footer/index.js +11 -11
  41. package/dist/es2019/version.json +1 -1
  42. package/dist/esm/analytics/constants.js +1 -0
  43. package/dist/esm/analytics/generated/analytics.types.js +1 -0
  44. package/dist/esm/analytics/generated/create-event-payload.js +38 -0
  45. package/dist/esm/analytics/generated/use-analytics-events.js +23 -0
  46. package/dist/esm/analytics/index.js +5 -0
  47. package/dist/esm/ui/common/error-state/access-required.js +9 -0
  48. package/dist/esm/ui/common/error-state/loading-error.js +9 -0
  49. package/dist/esm/ui/common/error-state/modal-loading-error.js +9 -0
  50. package/dist/esm/ui/common/error-state/no-results.js +7 -0
  51. package/dist/esm/ui/datasource-table-view/datasourceTableView.js +8 -20
  52. package/dist/esm/ui/issue-like-table/column-picker/index.js +2 -4
  53. package/dist/esm/ui/issue-like-table/drag-column-preview.js +29 -0
  54. package/dist/esm/ui/issue-like-table/draggable-table-heading.js +60 -25
  55. package/dist/esm/ui/issue-like-table/empty-state/index.js +6 -1
  56. package/dist/esm/ui/issue-like-table/index.js +102 -71
  57. package/dist/esm/ui/issue-like-table/styled.js +2 -1
  58. package/dist/esm/ui/jira-issues-modal/jira-search-container/index.js +8 -0
  59. package/dist/esm/ui/jira-issues-modal/modal/index.js +43 -14
  60. package/dist/esm/ui/table-footer/index.js +10 -7
  61. package/dist/esm/version.json +1 -1
  62. package/dist/types/analytics/constants.d.ts +1 -0
  63. package/dist/types/analytics/generated/analytics.types.d.ts +49 -0
  64. package/dist/types/analytics/generated/create-event-payload.d.ts +27 -0
  65. package/dist/types/analytics/generated/use-analytics-events.d.ts +3 -0
  66. package/dist/types/analytics/index.d.ts +4 -0
  67. package/dist/types/ui/common/error-state/access-required.d.ts +0 -1
  68. package/dist/types/ui/common/error-state/loading-error.d.ts +0 -1
  69. package/dist/types/ui/common/error-state/modal-loading-error.d.ts +0 -1
  70. package/dist/types/ui/common/error-state/no-results.d.ts +0 -1
  71. package/dist/types/ui/issue-like-table/column-picker/index.d.ts +1 -1
  72. package/dist/types/ui/issue-like-table/column-picker/types.d.ts +0 -1
  73. package/dist/types/ui/issue-like-table/drag-column-preview.d.ts +7 -0
  74. package/dist/types/ui/issue-like-table/draggable-table-heading.d.ts +2 -3
  75. package/dist/types/ui/issue-like-table/index.d.ts +1 -1
  76. package/dist/types/ui/issue-like-table/types.d.ts +5 -0
  77. package/dist/types/ui/jira-issues-modal/modal/index.d.ts +3 -1
  78. package/dist/types-ts4.5/analytics/constants.d.ts +1 -0
  79. package/dist/types-ts4.5/analytics/generated/analytics.types.d.ts +49 -0
  80. package/dist/types-ts4.5/analytics/generated/create-event-payload.d.ts +31 -0
  81. package/dist/types-ts4.5/analytics/generated/use-analytics-events.d.ts +7 -0
  82. package/dist/types-ts4.5/analytics/index.d.ts +8 -0
  83. package/dist/types-ts4.5/ui/common/error-state/access-required.d.ts +0 -1
  84. package/dist/types-ts4.5/ui/common/error-state/loading-error.d.ts +0 -1
  85. package/dist/types-ts4.5/ui/common/error-state/modal-loading-error.d.ts +0 -1
  86. package/dist/types-ts4.5/ui/common/error-state/no-results.d.ts +0 -1
  87. package/dist/types-ts4.5/ui/issue-like-table/column-picker/index.d.ts +1 -1
  88. package/dist/types-ts4.5/ui/issue-like-table/column-picker/types.d.ts +0 -1
  89. package/dist/types-ts4.5/ui/issue-like-table/drag-column-preview.d.ts +7 -0
  90. package/dist/types-ts4.5/ui/issue-like-table/draggable-table-heading.d.ts +2 -3
  91. package/dist/types-ts4.5/ui/issue-like-table/index.d.ts +1 -1
  92. package/dist/types-ts4.5/ui/issue-like-table/types.d.ts +5 -0
  93. package/dist/types-ts4.5/ui/jira-issues-modal/modal/index.d.ts +3 -1
  94. package/package.json +7 -2
@@ -3,30 +3,42 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
3
  import { css, jsx } from '@emotion/react';
4
4
  import styled from '@emotion/styled';
5
5
  import invariant from 'tiny-invariant';
6
+ import Heading from '@atlaskit/heading';
6
7
  import { Skeleton } from '@atlaskit/linking-common';
7
8
  import { extractClosestEdge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/addon/closest-edge';
8
9
  import { reorderWithEdge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/util/reorder-with-edge';
9
10
  import { autoScroller } from '@atlaskit/pragmatic-drag-and-drop-react-beautiful-dnd-autoscroll';
10
11
  import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/adapter/element';
11
12
  import { combine } from '@atlaskit/pragmatic-drag-and-drop/util/combine';
13
+ import { N40 } from '@atlaskit/theme/colors';
12
14
  import { ColumnPicker } from './column-picker';
15
+ import { DragColumnPreview } from './drag-column-preview';
13
16
  import { DraggableTableHeading } from './draggable-table-heading';
14
17
  import TableEmptyState from './empty-state';
15
18
  import { fallbackRenderType } from './render-type';
16
19
  import { Table, TableHeading } from './styled';
17
20
  import { useIsOnScreen } from './useIsOnScreen';
21
+ const tableSidePadding = "var(--ds-space-200, 16px)";
18
22
  const tableHeadStyles = css({
19
23
  background: "var(--ds-surface, #FFF)",
20
- borderTop: '2px solid transparent'
24
+ position: 'sticky',
25
+ top: 0,
26
+ zIndex: 10
21
27
  });
22
- const ColumnPickerHeader = styled.td`
28
+ const ColumnPickerHeader = styled.th`
23
29
  width: 40px;
24
- padding-block: ${"var(--ds-space-100, 8px)"};
25
30
  position: sticky;
26
- right: 0px;
31
+ right: calc(-1 * ${tableSidePadding});
27
32
  background-color: ${"var(--ds-surface, #FFF)"};
28
- &:last-child {
29
- padding-right: ${"var(--ds-space-100, 8px)"};
33
+ border-bottom: 2px solid ${`var(--ds-background-accent-gray-subtler, ${N40})`}; /* It is required to have solid (not half-transparent) color because of this gradient business bellow */
34
+ background: linear-gradient(
35
+ 90deg,
36
+ rgba(255, 255, 255, 0) 0%,
37
+ ${"var(--ds-surface, #FFF)"} 10%
38
+ );
39
+ vertical-align: middle; /* Keeps dropdown button in the middle */
40
+ &:last-of-type {
41
+ padding-right: ${tableSidePadding};
30
42
  }
31
43
  `;
32
44
  const truncatedCellStyles = css({
@@ -34,11 +46,22 @@ const truncatedCellStyles = css({
34
46
  textOverflow: 'ellipsis',
35
47
  whiteSpace: 'nowrap'
36
48
  });
37
- const tableDragPreviewStyles = css({
38
- backgroundColor: "var(--ds-surface, #FFF)"
49
+ const scrollableContainerStyles = css({
50
+ overflow: 'auto',
51
+ padding: `0 ${tableSidePadding} 0 ${tableSidePadding}`,
52
+ boxSizing: 'border-box'
39
53
  });
40
- const containerDragPreviewStyles = css({
41
- overflow: 'hidden'
54
+ const tableStyles = css({
55
+ // These styles are needed to prevent thead bottom border from scrolling away.
56
+ // This happens because it is sticky. https://stackoverflow.com/questions/50361698/border-style-do-not-work-with-sticky-position-element
57
+ borderCollapse: 'separate',
58
+ borderSpacing: 0
59
+ });
60
+
61
+ // By default tbody and thead have border-bottom: 2px ...
62
+ // This removes it, because for header we handle it via `th` styling and footer supply bottom border
63
+ const noDefaultBorderStyles = css({
64
+ borderBottom: 0
42
65
  });
43
66
  function extractIndex(data) {
44
67
  const {
@@ -69,7 +92,6 @@ const BASE_WIDTH = 8;
69
92
  function getColumnWidth(key, type) {
70
93
  const keyBasedWidth = {
71
94
  assignee: BASE_WIDTH * 22,
72
- key: BASE_WIDTH * 10,
73
95
  labels: BASE_WIDTH * 22,
74
96
  priority: BASE_WIDTH * 8,
75
97
  status: BASE_WIDTH * 18,
@@ -98,11 +120,11 @@ export const IssueLikeDataTableView = ({
98
120
  visibleColumnKeys,
99
121
  onVisibleColumnKeysChange,
100
122
  status,
101
- hasNextPage
123
+ hasNextPage,
124
+ scrollableContainerHeight
102
125
  }) => {
103
126
  const tableId = useMemo(() => Symbol('unique-id'), []);
104
127
  const [lastRowElement, setLastRowElement] = useState(null);
105
- const [isDragPreview, setIsDragPreview] = useState(false);
106
128
  const [hasFullSchema, setHasFullSchema] = useState(false);
107
129
  const isBottomOfTableVisibleRaw = useIsOnScreen(lastRowElement);
108
130
  const containerRef = useRef(null);
@@ -135,7 +157,7 @@ export const IssueLikeDataTableView = ({
135
157
  };
136
158
  })
137
159
  }), [visibleSortedColumns]);
138
- const headColumns = visibleSortedColumns.map(({
160
+ const headColumns = useMemo(() => visibleSortedColumns.map(({
139
161
  key,
140
162
  title,
141
163
  type
@@ -144,7 +166,7 @@ export const IssueLikeDataTableView = ({
144
166
  content: title,
145
167
  shouldTruncate: true,
146
168
  maxWidth: getColumnWidth(key, type)
147
- }));
169
+ })), [visibleSortedColumns]);
148
170
  useEffect(() => {
149
171
  if (isBottomOfTableVisibleRaw && hasNextPage && status === 'resolved') {
150
172
  void onNextPage({
@@ -152,37 +174,41 @@ export const IssueLikeDataTableView = ({
152
174
  });
153
175
  }
154
176
  }, [isBottomOfTableVisibleRaw, status, hasNextPage, onNextPage]);
155
- let dndPreviewHeight = 0;
156
- if (items.length > 0 && containerRef.current) {
157
- const containerEl = containerRef.current;
158
- invariant(containerEl);
159
- dndPreviewHeight = containerEl.offsetHeight;
160
- }
177
+ const hasData = items.length > 0;
161
178
 
162
179
  // This variable contains initial Y mouse coordinate, so we can restrict
163
180
  // autoScroller in X axis only
164
181
  const initialAutoScrollerClientY = useRef();
165
182
  useEffect(() => {
166
- if (!onVisibleColumnKeysChange) {
183
+ if (!onVisibleColumnKeysChange || !hasData) {
167
184
  return;
168
185
  }
169
186
  return combine(monitorForElements({
170
187
  onDragStart: ({
171
188
  location
172
189
  }) => {
190
+ var _containerRef$current;
173
191
  initialAutoScrollerClientY.current = location.current.input.clientY;
174
192
  autoScroller.start({
175
- input: location.current.input,
193
+ input: {
194
+ ...location.current.input,
195
+ clientY:
196
+ // The goal is to have clientY the same and in the middle of the scrollable area
197
+ // Since clientY is taken from to of the viewport we need to plus that in order to get
198
+ // middle of the scrollable area in reference to the viewport
199
+ (initialAutoScrollerClientY.current || 0) + (((_containerRef$current = containerRef.current) === null || _containerRef$current === void 0 ? void 0 : _containerRef$current.offsetHeight) || 0) / 2
200
+ },
176
201
  behavior: 'container-only'
177
202
  });
178
203
  },
179
204
  onDrag: ({
180
205
  location
181
206
  }) => {
207
+ var _containerRef$current2;
182
208
  autoScroller.updateInput({
183
209
  input: {
184
210
  ...location.current.input,
185
- clientY: initialAutoScrollerClientY.current || 0
211
+ clientY: (initialAutoScrollerClientY.current || 0) + (((_containerRef$current2 = containerRef.current) === null || _containerRef$current2 === void 0 ? void 0 : _containerRef$current2.offsetHeight) || 0) / 2
186
212
  }
187
213
  });
188
214
  },
@@ -190,7 +216,6 @@ export const IssueLikeDataTableView = ({
190
216
  source,
191
217
  location
192
218
  }) {
193
- initialAutoScrollerClientY.current = null;
194
219
  autoScroller.stop();
195
220
  if (location.current.dropTargets.length === 0) {
196
221
  return;
@@ -220,7 +245,7 @@ export const IssueLikeDataTableView = ({
220
245
  }
221
246
  }
222
247
  }));
223
- }, [visibleColumnKeys, onVisibleColumnKeysChange, tableId]);
248
+ }, [visibleColumnKeys, onVisibleColumnKeysChange, tableId, hasData]);
224
249
  const tableRows = useMemo(() => items.map((newRowData, rowIndex) => ({
225
250
  key: `${identityColumnKey && newRowData[identityColumnKey] && newRowData[identityColumnKey].data || rowIndex}`,
226
251
  cells: visibleSortedColumns.map(({
@@ -242,9 +267,7 @@ export const IssueLikeDataTableView = ({
242
267
  }),
243
268
  ref: rowIndex === items.length - 1 ? el => setLastRowElement(el) : undefined
244
269
  })), [identityColumnKey, renderItem, items, visibleSortedColumns]);
245
- const rows = [...tableRows, ...(status === 'loading' ? [loadingRow] : [])];
246
- const setIsDragPreviewOn = useCallback(() => setIsDragPreview(true), [setIsDragPreview]);
247
- const setIsDragPreviewOff = useCallback(() => setIsDragPreview(false), [setIsDragPreview]);
270
+ const rows = useMemo(() => [...tableRows, ...(status === 'loading' ? [loadingRow] : [])], [loadingRow, status, tableRows]);
248
271
  const onSelectedColumnKeysChange = useCallback(newSelectedColumnKeys => {
249
272
  onVisibleColumnKeysChange === null || onVisibleColumnKeysChange === void 0 ? void 0 : onVisibleColumnKeysChange(newSelectedColumnKeys);
250
273
  }, [onVisibleColumnKeysChange]);
@@ -261,32 +284,50 @@ export const IssueLikeDataTableView = ({
261
284
  }, [hasFullSchema, onLoadDatasourceDetails]);
262
285
  return jsx("div", {
263
286
  ref: containerRef,
264
- css: isDragPreview ? containerDragPreviewStyles : null
287
+ css: scrollableContainerHeight ? scrollableContainerStyles : null,
288
+ style: scrollableContainerHeight ? {
289
+ maxHeight: `${scrollableContainerHeight}px`
290
+ } : undefined
265
291
  }, jsx(Table, {
266
- css: isDragPreview ? tableDragPreviewStyles : null,
292
+ css: tableStyles,
267
293
  "data-testid": testId
268
294
  }, jsx("thead", {
269
295
  "data-testid": testId && `${testId}--head`,
270
- css: tableHeadStyles
296
+ css: [noDefaultBorderStyles, tableHeadStyles]
271
297
  }, jsx("tr", null, headColumns.map(({
272
298
  key,
273
299
  content,
274
300
  maxWidth
275
301
  }, cellIndex) => {
276
- const TruncatedContent = () => jsx("div", {
277
- css: truncatedCellStyles
278
- }, content);
279
- if (onVisibleColumnKeysChange && status !== 'loading') {
302
+ if (onVisibleColumnKeysChange && hasData) {
303
+ var _containerRef$current3;
304
+ const previewRows = tableRows.map(({
305
+ cells
306
+ }) => {
307
+ const cell = cells.find(({
308
+ key: cellKey
309
+ }) => cellKey === key);
310
+ if (cell) {
311
+ return cell.content;
312
+ }
313
+ }).slice(0, 5);
314
+ const dragPreview = jsx(DragColumnPreview, {
315
+ title: jsx(Heading, {
316
+ level: "h400"
317
+ }, content),
318
+ rows: previewRows
319
+ });
280
320
  return jsx(DraggableTableHeading, {
281
321
  tableId: tableId,
282
322
  key: key,
283
323
  id: key,
284
324
  index: cellIndex,
285
325
  maxWidth: maxWidth,
286
- dndPreviewHeight: dndPreviewHeight,
287
- onDragPreviewStart: setIsDragPreviewOn,
288
- onDragPreviewEnd: setIsDragPreviewOff
289
- }, jsx(TruncatedContent, null));
326
+ dndPreviewHeight: ((_containerRef$current3 = containerRef.current) === null || _containerRef$current3 === void 0 ? void 0 : _containerRef$current3.offsetHeight) || 0,
327
+ dragPreview: dragPreview
328
+ }, jsx(Heading, {
329
+ level: "h400"
330
+ }, content));
290
331
  } else {
291
332
  return jsx(TableHeading, {
292
333
  key: key,
@@ -294,15 +335,17 @@ export const IssueLikeDataTableView = ({
294
335
  style: {
295
336
  maxWidth
296
337
  }
297
- }, jsx(TruncatedContent, null));
338
+ }, jsx(Heading, {
339
+ level: "h400"
340
+ }, content));
298
341
  }
299
- }), onVisibleColumnKeysChange && jsx(ColumnPickerHeader, null, jsx(ColumnPicker, {
342
+ }), onVisibleColumnKeysChange && hasData && jsx(ColumnPickerHeader, null, jsx(ColumnPicker, {
300
343
  columns: hasFullSchema ? orderedColumns : [],
301
344
  selectedColumnKeys: hasFullSchema ? visibleColumnKeys : [],
302
- isDatasourceLoading: status === 'loading',
303
345
  onSelectedColumnKeysChange: onSelectedColumnKeysChange,
304
346
  onOpen: handlePickerOpen
305
347
  })))), jsx("tbody", {
348
+ css: noDefaultBorderStyles,
306
349
  "data-testid": testId && `${testId}--body`
307
350
  }, rows.map(({
308
351
  key,
@@ -1,11 +1,29 @@
1
1
  import styled from '@emotion/styled';
2
+ import { N40 } from '@atlaskit/theme/colors';
2
3
  export const Table = styled.table`
3
4
  width: 100%;
4
5
  `;
5
6
  export const TableHeading = styled.th`
7
+ cursor: grab;
6
8
  position: relative;
7
9
  padding-block: ${"var(--ds-space-100, 8px)"};
8
10
  line-height: ${"var(--ds-font-lineHeight-300, 24px)"};
11
+ border-bottom: 2px solid ${`var(--ds-background-accent-gray-subtler, ${N40})`};
12
+ .ProseMirror & h5,
13
+ & h5 {
14
+ margin-top: 0;
15
+ overflow: hidden;
16
+ text-overflow: ellipsis;
17
+ white-space: nowrap;
18
+ line-height: 24px; /* Is needed to keep overall height consistent with or without drag handle icon present */
19
+ }
20
+
21
+ &:hover .issue-like-table-drag-handle {
22
+ width: 24px;
23
+ }
24
+ &:hover .issue-like-table-drag-handle-spacer {
25
+ width: 0px;
26
+ }
9
27
  `;
10
28
  export const EmptyStateTableHeading = styled(TableHeading)`
11
29
  &:first-child {
@@ -2,6 +2,7 @@
2
2
  import React, { useState } from 'react';
3
3
  import { css, jsx } from '@emotion/react';
4
4
  import { useIntl } from 'react-intl-next';
5
+ import { useDatasourceAnalyticsEvents } from '../../../analytics';
5
6
  import { BasicSearchInput } from '../basic-search-input';
6
7
  import { JiraJQLEditor } from '../jql-editor';
7
8
  import { ModeSwitcher } from '../mode-switcher';
@@ -34,6 +35,9 @@ export const JiraSearchContainer = props => {
34
35
  const [jql, setJql] = useState(initialJql || DEFAULT_JQL_QUERY);
35
36
  const [orderKey, setOrderKey] = useState();
36
37
  const [orderDirection, setOrderDirection] = useState();
38
+ const {
39
+ fireEvent
40
+ } = useDatasourceAnalyticsEvents();
37
41
  const onSearchModeChange = searchMode => {
38
42
  setCurrentSearchMode(searchMode);
39
43
  };
@@ -65,6 +69,11 @@ export const JiraSearchContainer = props => {
65
69
  onSearch({
66
70
  jql
67
71
  });
72
+ if (currentSearchMode === basicModeValue) {
73
+ fireEvent('ui.form.submitted.basicSearch', {});
74
+ } else if (currentSearchMode === jqlModeValue) {
75
+ fireEvent('ui.jqlEditor.searched', {});
76
+ }
68
77
  };
69
78
  return jsx("div", {
70
79
  css: inputContainerStyles
@@ -3,11 +3,14 @@ import _extends from "@babel/runtime/helpers/extends";
3
3
  import { useCallback, useEffect, useMemo, useState } from 'react';
4
4
  import { css, jsx } from '@emotion/react';
5
5
  import { FormattedMessage, FormattedNumber, useIntl } from 'react-intl-next';
6
+ import { withAnalyticsContext } from '@atlaskit/analytics-next';
6
7
  import Button from '@atlaskit/button/standard-button';
7
8
  import Modal, { ModalBody, ModalFooter, ModalHeader, ModalTitle, ModalTransition } from '@atlaskit/modal-dialog';
8
- import { B400, N0, N800 } from '@atlaskit/theme/colors';
9
+ import { B400, N0, N40, N800 } from '@atlaskit/theme/colors';
10
+ import { useDatasourceAnalyticsEvents } from '../../../analytics';
9
11
  import { useDatasourceTableState } from '../../../hooks/useDatasourceTableState';
10
12
  import { getAvailableJiraSites } from '../../../services/getAvailableJiraSites';
13
+ import { name as packageName, version as packageVersion } from '../../../version.json';
11
14
  import { AccessRequired } from '../../common/error-state/access-required';
12
15
  import { ModalLoadingError } from '../../common/error-state/modal-loading-error';
13
16
  import { NoResults } from '../../common/error-state/no-results';
@@ -23,8 +26,9 @@ const dropdownContainerStyles = css({
23
26
  });
24
27
  const contentContainerStyles = css({
25
28
  display: 'grid',
26
- height: '420px',
27
- overflow: 'auto'
29
+ maxHeight: '420px',
30
+ overflow: 'auto',
31
+ borderBottom: `2px solid ${`var(--ds-background-accent-gray-subtler, ${N40})`}`
28
32
  });
29
33
  const placeholderSmartLinkStyles = css({
30
34
  backgroundColor: `var(--ds-surface-raised, ${N0})`,
@@ -41,7 +45,7 @@ const issueCountStyles = css({
41
45
  const smartLinkContainerStyles = css({
42
46
  paddingLeft: '1px'
43
47
  });
44
- export const JiraIssuesConfigModal = props => {
48
+ export const PlainJiraIssuesConfigModal = props => {
45
49
  const {
46
50
  datasourceId,
47
51
  parameters: initialParameters,
@@ -77,11 +81,17 @@ export const JiraIssuesConfigModal = props => {
77
81
  const {
78
82
  formatMessage
79
83
  } = useIntl();
84
+ const {
85
+ fireEvent
86
+ } = useDatasourceAnalyticsEvents();
80
87
  const selectedJiraSite = useMemo(() => availableSites.find(jiraSite => jiraSite.cloudId === cloudId) || availableSites[0], [availableSites, cloudId]);
81
88
  const resolvedWithNoResults = status === 'resolved' && !responseItems.length;
82
89
  const jqlUrl = selectedJiraSite && jql && `${selectedJiraSite.url}/issues/?jql=${encodeURI(jql)}`;
83
90
  const isInsertDisabled = !isParametersSet || status === 'rejected' || status === 'unauthorized' || status === 'loading' || resolvedWithNoResults;
84
91
  const shouldShowIssueCount = !!totalCount && totalCount !== 1 && currentViewMode === 'issue';
92
+ useEffect(() => {
93
+ fireEvent('screen.datasourceModalDialog.viewed', {});
94
+ }, [fireEvent]);
85
95
  useEffect(() => {
86
96
  const newVisibleColumnKeys = !initialVisibleColumnKeys || (initialVisibleColumnKeys || []).length === 0 ? defaultVisibleColumnKeys : initialVisibleColumnKeys;
87
97
  setVisibleColumnKeys(newVisibleColumnKeys);
@@ -90,9 +100,12 @@ export const JiraIssuesConfigModal = props => {
90
100
  const fetchSiteDisplayNames = async () => {
91
101
  const jiraSites = await getAvailableJiraSites();
92
102
  setAvailableSites(jiraSites);
103
+ fireEvent('ui.modal.ready.datasource', {
104
+ instancesCount: jiraSites.length
105
+ });
93
106
  };
94
107
  void fetchSiteDisplayNames();
95
- }, []);
108
+ }, [fireEvent]);
96
109
  useEffect(() => {
97
110
  if (!cloudId && selectedJiraSite) {
98
111
  setCloudId(selectedJiraSite.cloudId);
@@ -159,7 +172,9 @@ export const JiraIssuesConfigModal = props => {
159
172
  const handleViewModeChange = selectedMode => {
160
173
  setCurrentViewMode(selectedMode);
161
174
  };
162
- const issueLikeDataTableView = useMemo(() => jsx(IssueLikeDataTableView, {
175
+ const issueLikeDataTableView = useMemo(() => jsx("div", {
176
+ css: contentContainerStyles
177
+ }, jsx(IssueLikeDataTableView, {
163
178
  testId: "jira-jql-datasource-table",
164
179
  status: status,
165
180
  columns: columns,
@@ -169,7 +184,7 @@ export const JiraIssuesConfigModal = props => {
169
184
  onNextPage: onNextPage,
170
185
  onLoadDatasourceDetails: loadDatasourceDetails,
171
186
  onVisibleColumnKeysChange: setVisibleColumnKeys
172
- }), [columns, defaultVisibleColumnKeys, hasNextPage, loadDatasourceDetails, onNextPage, responseItems, status, visibleColumnKeys]);
187
+ })), [columns, defaultVisibleColumnKeys, hasNextPage, loadDatasourceDetails, onNextPage, responseItems, status, visibleColumnKeys]);
173
188
  const renderCountModeContent = useCallback(() => {
174
189
  const url = selectedJiraSite === null || selectedJiraSite === void 0 ? void 0 : selectedJiraSite.url;
175
190
  if (status === 'unauthorized') {
@@ -205,9 +220,11 @@ export const JiraIssuesConfigModal = props => {
205
220
  return jsx(NoResults, null);
206
221
  } else if (status === 'empty' || !columns.length) {
207
222
  // persist the empty state when making the initial /data request which contains the columns
208
- return jsx(EmptyState, {
223
+ return jsx("div", {
224
+ css: contentContainerStyles
225
+ }, jsx(EmptyState, {
209
226
  testId: `jira-jql-datasource-modal--empty-state`
210
- });
227
+ }));
211
228
  }
212
229
  const firstIssueUrl = retrieveUrlForSmartCardRender();
213
230
  if (responseItems.length === 1 && firstIssueUrl) {
@@ -250,9 +267,7 @@ export const JiraIssuesConfigModal = props => {
250
267
  isSearching: status === 'loading',
251
268
  parameters: parameters,
252
269
  onSearch: onSearch
253
- }), jsx("div", {
254
- css: contentContainerStyles
255
- }, currentViewMode === 'count' ? renderCountModeContent() : renderIssuesModeContent())), jsx(ModalFooter, null, shouldShowIssueCount && jsx("div", {
270
+ }), currentViewMode === 'count' ? renderCountModeContent() : renderIssuesModeContent()), jsx(ModalFooter, null, shouldShowIssueCount && jsx("div", {
256
271
  "data-testid": "jira-jql-datasource-modal-total-issues-count",
257
272
  css: issueCountStyles
258
273
  }, jsx(FormattedNumber, {
@@ -270,4 +285,19 @@ export const JiraIssuesConfigModal = props => {
270
285
  isDisabled: isInsertDisabled,
271
286
  testId: 'jira-jql-datasource-modal--insert-button'
272
287
  }, jsx(FormattedMessage, modalMessages.insertIssuesButtonText)))));
273
- };
288
+ };
289
+ const analyticsContextAttributes = {
290
+ dataProvider: 'jira-issues'
291
+ };
292
+ const analyticsContextData = {
293
+ packageName,
294
+ packageVersion,
295
+ source: 'datasourceConfigModal'
296
+ };
297
+ const contextData = {
298
+ ...analyticsContextData,
299
+ attributes: {
300
+ ...analyticsContextAttributes
301
+ }
302
+ };
303
+ export const JiraIssuesConfigModal = withAnalyticsContext(contextData)(PlainJiraIssuesConfigModal);
@@ -11,20 +11,18 @@ import { N0, N40, N800, N90 } from '@atlaskit/theme/colors';
11
11
  import { footerMessages } from './messages';
12
12
  import { SyncInfo } from './sync-info';
13
13
  const FooterWrapper = styled.div`
14
+ padding: 0 ${"var(--ds-space-200, 16px)"};
15
+ box-sizing: border-box;
16
+ background: ${`var(--ds-background-input, ${N0})`};
17
+ `;
18
+ const TopBorderWrapper = styled.div`
14
19
  display: flex;
20
+ box-sizing: border-box;
15
21
  justify-content: space-between;
16
- width: 100%;
17
22
  padding: ${"var(--ds-space-250, 20px)"} 0;
18
- position: sticky;
19
- bottom: 0;
20
- background: ${`var(--ds-background-input, ${N0})`};
21
- border-top-style: solid;
22
- border-top-color: ${`var(--ds-background-neutral, ${N40})`};
23
- margin-top: -2px;
24
- align-self: center;
23
+ border-top: 2px solid ${`var(--ds-background-accent-gray-subtler, ${N40})`};
25
24
  `;
26
25
  const IssueCounterWrapper = styled.div`
27
- margin-left: 10px;
28
26
  display: flex;
29
27
  align-self: center;
30
28
  color: ${`var(--ds-text-accent-gray, ${N800})`};
@@ -55,7 +53,9 @@ export const TableFooter = ({
55
53
  // ensure correct positioning since 'justify-content: space-between' is used).
56
54
  return onRefresh || showIssueCount ? jsx(FooterWrapper, {
57
55
  "data-testid": "table-footer"
58
- }, jsx(IssueCounterWrapper, null, showIssueCount && jsx(Heading, {
56
+ }, jsx(TopBorderWrapper, null, jsx(IssueCounterWrapper, {
57
+ "data-testid": 'issue-count-wrapper'
58
+ }, showIssueCount && jsx(Heading, {
59
59
  testId: "issue-count",
60
60
  level: "h400"
61
61
  }, jsx(FormattedNumber, {
@@ -76,5 +76,5 @@ export const TableFooter = ({
76
76
  }),
77
77
  isDisabled: isLoading,
78
78
  testId: "refresh-button"
79
- })))) : null;
79
+ }))))) : null;
80
80
  };
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/link-datasource",
3
- "version": "0.31.2",
3
+ "version": "0.32.0",
4
4
  "sideEffects": false
5
5
  }
@@ -0,0 +1 @@
1
+ export var EVENT_CHANNEL = 'media';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,38 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ /**
3
+ * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
4
+ *
5
+ * Generates Typescript types for analytics events from analytics.spec.yaml
6
+ *
7
+ * @codegen <<SignedSource::819168596ba17484cadda969f8ecf82d>>
8
+ * @codegenCommand yarn workspace @atlassian/analytics-tooling run analytics:codegen link-datasource
9
+ */
10
+
11
+ var createEventPayload = function createEventPayload(eventKey) {
12
+ for (var _len = arguments.length, _ref = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
13
+ _ref[_key - 1] = arguments[_key];
14
+ }
15
+ var attributes = _ref[0];
16
+ var _eventKey$split = eventKey.split('.'),
17
+ _eventKey$split2 = _slicedToArray(_eventKey$split, 4),
18
+ eventType = _eventKey$split2[0],
19
+ actionSubject = _eventKey$split2[1],
20
+ action = _eventKey$split2[2],
21
+ actionSubjectId = _eventKey$split2[3];
22
+ if (eventType === 'screen') {
23
+ return {
24
+ eventType: eventType,
25
+ name: actionSubject,
26
+ action: 'viewed',
27
+ attributes: attributes
28
+ };
29
+ }
30
+ return {
31
+ eventType: eventType,
32
+ actionSubject: actionSubject,
33
+ action: action,
34
+ actionSubjectId: actionSubjectId,
35
+ attributes: attributes
36
+ };
37
+ };
38
+ export default createEventPayload;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
3
+ *
4
+ * Generates Typescript types for analytics events from analytics.spec.yaml
5
+ *
6
+ * @codegen <<SignedSource::415836762b378b7d79f3aff4ba051c14>>
7
+ * @codegenCommand yarn workspace @atlassian/analytics-tooling run analytics:codegen link-datasource
8
+ */
9
+ import { useCallback } from 'react';
10
+ import { useAnalyticsEvents as useAnalyticsNextEvents } from '@atlaskit/analytics-next';
11
+ import { EVENT_CHANNEL } from '../constants';
12
+ import createEventPayload from './create-event-payload';
13
+ export var useAnalyticsEvents = function useAnalyticsEvents() {
14
+ var _useAnalyticsNextEven = useAnalyticsNextEvents(),
15
+ createAnalyticsEvent = _useAnalyticsNextEven.createAnalyticsEvent;
16
+ var fireEvent = useCallback(function () {
17
+ var event = createAnalyticsEvent(createEventPayload.apply(void 0, arguments));
18
+ event.fire(EVENT_CHANNEL);
19
+ }, [createAnalyticsEvent]);
20
+ return {
21
+ fireEvent: fireEvent
22
+ };
23
+ };
@@ -0,0 +1,5 @@
1
+ export { EVENT_CHANNEL } from './constants';
2
+ import { useAnalyticsEvents } from './generated/use-analytics-events';
3
+ export var useDatasourceAnalyticsEvents = function useDatasourceAnalyticsEvents() {
4
+ return useAnalyticsEvents();
5
+ };
@@ -1,13 +1,22 @@
1
1
  /** @jsx jsx */
2
+ import { useEffect } from 'react';
2
3
  import { jsx } from '@emotion/react';
3
4
  import { useIntl } from 'react-intl-next';
4
5
  import EmptyState from '@atlaskit/empty-state';
6
+ import { useDatasourceAnalyticsEvents } from '../../../analytics';
5
7
  import { AccessRequiredSVG } from './access-required-svg';
6
8
  import { loadingErrorMessages } from './messages';
7
9
  export var AccessRequired = function AccessRequired(_ref) {
8
10
  var siteName = _ref.siteName;
9
11
  var _useIntl = useIntl(),
10
12
  formatMessage = _useIntl.formatMessage;
13
+ var _useDatasourceAnalyti = useDatasourceAnalyticsEvents(),
14
+ fireEvent = _useDatasourceAnalyti.fireEvent;
15
+ useEffect(function () {
16
+ fireEvent('ui.error.shown', {
17
+ reason: 'access'
18
+ });
19
+ }, [fireEvent]);
11
20
  return jsx(EmptyState, {
12
21
  header: siteName ? formatMessage(loadingErrorMessages.accessRequiredWithSite, {
13
22
  siteName: siteName
@@ -1,7 +1,9 @@
1
1
  /** @jsx jsx */
2
+ import { useEffect } from 'react';
2
3
  import { css, jsx } from '@emotion/react';
3
4
  import { FormattedMessage } from 'react-intl-next';
4
5
  import Button from '@atlaskit/button/standard-button';
6
+ import { useDatasourceAnalyticsEvents } from '../../../analytics';
5
7
  import { LoadingErrorSVG } from './loading-error-svg';
6
8
  import { loadingErrorMessages } from './messages';
7
9
  var errorContainerStyles = css({
@@ -24,6 +26,13 @@ var errorDescriptionStyles = css({
24
26
  });
25
27
  export var LoadingError = function LoadingError(_ref) {
26
28
  var onRefresh = _ref.onRefresh;
29
+ var _useDatasourceAnalyti = useDatasourceAnalyticsEvents(),
30
+ fireEvent = _useDatasourceAnalyti.fireEvent;
31
+ useEffect(function () {
32
+ fireEvent('ui.error.shown', {
33
+ reason: 'network'
34
+ });
35
+ }, [fireEvent]);
27
36
  return jsx("div", {
28
37
  css: errorContainerStyles,
29
38
  "data-testid": "jira-jql-datasource--loading-error"
@@ -1,7 +1,9 @@
1
1
  import _extends from "@babel/runtime/helpers/extends";
2
2
  /** @jsx jsx */
3
+ import { useEffect } from 'react';
3
4
  import { css, jsx } from '@emotion/react';
4
5
  import { FormattedMessage } from 'react-intl-next';
6
+ import { useDatasourceAnalyticsEvents } from '../../../analytics';
5
7
  import { LoadingErrorSVG } from './loading-error-svg';
6
8
  import { loadingErrorMessages } from './messages';
7
9
  var errorContainerStyles = css({
@@ -24,6 +26,13 @@ var errorDescriptionStyles = css({
24
26
  });
25
27
  export var ModalLoadingError = function ModalLoadingError(_ref) {
26
28
  var url = _ref.url;
29
+ var _useDatasourceAnalyti = useDatasourceAnalyticsEvents(),
30
+ fireEvent = _useDatasourceAnalyti.fireEvent;
31
+ useEffect(function () {
32
+ fireEvent('ui.error.shown', {
33
+ reason: 'network'
34
+ });
35
+ }, [fireEvent]);
27
36
  return jsx("div", {
28
37
  css: errorContainerStyles,
29
38
  "data-testid": "jira-jql-datasource-modal--loading-error"