@atlaskit/link-datasource 5.5.1 → 5.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/cjs/ui/datasource-table-view/datasource-column-sort.js +51 -0
  3. package/dist/cjs/ui/datasource-table-view/datasourceTableView.js +52 -5
  4. package/dist/cjs/ui/issue-like-table/index.compiled.css +5 -0
  5. package/dist/cjs/ui/issue-like-table/index.js +120 -62
  6. package/dist/cjs/ui/issue-like-table/messages.js +10 -0
  7. package/dist/es2019/ui/datasource-table-view/datasource-column-sort.js +43 -0
  8. package/dist/es2019/ui/datasource-table-view/datasourceTableView.js +46 -6
  9. package/dist/es2019/ui/issue-like-table/index.compiled.css +5 -0
  10. package/dist/es2019/ui/issue-like-table/index.js +58 -4
  11. package/dist/es2019/ui/issue-like-table/messages.js +10 -0
  12. package/dist/esm/ui/datasource-table-view/datasource-column-sort.js +44 -0
  13. package/dist/esm/ui/datasource-table-view/datasourceTableView.js +53 -6
  14. package/dist/esm/ui/issue-like-table/index.compiled.css +5 -0
  15. package/dist/esm/ui/issue-like-table/index.js +120 -62
  16. package/dist/esm/ui/issue-like-table/messages.js +10 -0
  17. package/dist/types/ui/datasource-table-view/datasource-column-sort.d.ts +12 -0
  18. package/dist/types/ui/datasource-table-view/datasourceTableView.d.ts +2 -2
  19. package/dist/types/ui/datasource-table-view/types.d.ts +1 -1
  20. package/dist/types/ui/issue-like-table/index.d.ts +1 -1
  21. package/dist/types/ui/issue-like-table/messages.d.ts +33 -23
  22. package/dist/types/ui/issue-like-table/types.d.ts +10 -0
  23. package/dist/types-ts4.5/ui/datasource-table-view/datasource-column-sort.d.ts +12 -0
  24. package/dist/types-ts4.5/ui/datasource-table-view/datasourceTableView.d.ts +2 -2
  25. package/dist/types-ts4.5/ui/datasource-table-view/types.d.ts +1 -1
  26. package/dist/types-ts4.5/ui/issue-like-table/index.d.ts +1 -1
  27. package/dist/types-ts4.5/ui/issue-like-table/messages.d.ts +33 -23
  28. package/dist/types-ts4.5/ui/issue-like-table/types.d.ts +10 -0
  29. package/package.json +6 -3
@@ -1,8 +1,10 @@
1
1
  /* datasourceTableView.tsx generated by @compiled/babel-plugin v0.39.1 */
2
+ import _extends from "@babel/runtime/helpers/extends";
2
3
  import "./datasourceTableView.compiled.css";
3
4
  import * as React from 'react';
4
5
  import { ax, ix } from "@compiled/react/runtime";
5
- import { useCallback, useEffect, useRef } from 'react';
6
+ import { useCallback, useEffect, useRef, useState } from 'react';
7
+ import isEqual from 'lodash/isEqual';
6
8
  import { withAnalyticsContext } from '@atlaskit/analytics-next';
7
9
  import { IntlMessagesProvider } from '@atlaskit/intl-messages-provider';
8
10
  import { fg } from '@atlaskit/platform-feature-flags';
@@ -26,6 +28,7 @@ import { ProviderAuthRequired } from '../common/error-state/provider-auth-requir
26
28
  import { IssueLikeDataTableView } from '../issue-like-table';
27
29
  import EmptyState from '../issue-like-table/empty-state';
28
30
  import { TableFooter } from '../table-footer';
31
+ import { getDatasourceColumnSortGetter } from './datasource-column-sort';
29
32
  const containerStyles = null;
30
33
  const DefaultScrollableContainerHeight = 590;
31
34
  const DatasourceTableViewWithoutAnalytics = ({
@@ -40,6 +43,26 @@ const DatasourceTableViewWithoutAnalytics = ({
40
43
  onWrappedColumnChange,
41
44
  scrollableContainerHeight = DefaultScrollableContainerHeight
42
45
  }) => {
46
+ // Local copy lets us apply in-session sort mutations without mutating external parameters.
47
+ const [sessionParameters, setSessionParameters] = useState(parameters);
48
+ // Tracks the external parameters that the current session/sort state was derived from.
49
+ const sessionBaseParametersRef = useRef(parameters);
50
+ const [sortState, setSortState] = useState();
51
+ const columnSortGetter = getDatasourceColumnSortGetter(datasourceId);
52
+ // Sorting is only owned by this view when parent callbacks are absent (read-only rendering mode).
53
+ const isReadOnlyDatasourceTable = !onVisibleColumnKeysChange && !onColumnResize && !onWrappedColumnChange;
54
+ // Keep sort UI hidden unless datasource + ownership + feature gate all allow it.
55
+ const shouldEnableColumnSort = !!columnSortGetter && isReadOnlyDatasourceTable && fg('platform_lp_jira_sllv_renderer_column_sorting');
56
+ useDeepEffect(() => {
57
+ // External parameter updates should always reset local sort/session state back to source-of-truth.
58
+ sessionBaseParametersRef.current = parameters;
59
+ setSessionParameters(parameters);
60
+ setSortState(undefined);
61
+ }, [parameters]);
62
+ const isSessionBasedOnCurrentParameters = isEqual(sessionBaseParametersRef.current, parameters);
63
+ // Use session parameters only when they are known to be based on the current external parameters.
64
+ // This avoids a one-render stale read during the deep-effect update cycle above.
65
+ const activeParameters = isSessionBasedOnCurrentParameters && fg('platform_lp_jira_sllv_renderer_column_sorting') ? sessionParameters : parameters;
43
66
  const {
44
67
  reset,
45
68
  status,
@@ -57,7 +80,7 @@ const DatasourceTableViewWithoutAnalytics = ({
57
80
  authDetails
58
81
  } = useDatasourceTableState({
59
82
  datasourceId,
60
- parameters,
83
+ parameters: activeParameters,
61
84
  fieldKeys: visibleColumnKeys
62
85
  });
63
86
  const isInPDFRender = useIsInPDFRender();
@@ -95,7 +118,7 @@ const DatasourceTableViewWithoutAnalytics = ({
95
118
  reset();
96
119
  }
97
120
  isInitialRender.current = false;
98
- }, [reset, parameters]);
121
+ }, [reset, activeParameters]);
99
122
  useEffect(() => {
100
123
  if (onVisibleColumnKeysChange && (visibleColumnKeys || []).length === 0 && defaultVisibleColumnKeys.length > 0) {
101
124
  onVisibleColumnKeysChange(defaultVisibleColumnKeys);
@@ -141,6 +164,19 @@ const DatasourceTableViewWithoutAnalytics = ({
141
164
  });
142
165
  forcedReset();
143
166
  }, [destinationObjectTypes, extensionKey, fireEvent, forcedReset]);
167
+ const onColumnSort = useCallback(columnKey => {
168
+ // Build next params/sort atomically so UI state and query state stay in sync.
169
+ const result = columnSortGetter === null || columnSortGetter === void 0 ? void 0 : columnSortGetter({
170
+ parameters: sessionParameters,
171
+ columnKey,
172
+ currentSort: sortState
173
+ });
174
+ if (!result) {
175
+ return;
176
+ }
177
+ setSortState(result.sort);
178
+ setSessionParameters(result.parameters);
179
+ }, [columnSortGetter, sessionParameters, sortState]);
144
180
  const handleErrorRefresh = useCallback(() => {
145
181
  reset({
146
182
  shouldForceRequest: true
@@ -172,7 +208,7 @@ const DatasourceTableViewWithoutAnalytics = ({
172
208
  loaderFn: fetchMessagesForLocale
173
209
  }, /*#__PURE__*/React.createElement("div", {
174
210
  className: ax(["_2rko1kw7", "datasource-table"])
175
- }, hasColumns ? /*#__PURE__*/React.createElement(IssueLikeDataTableView, {
211
+ }, hasColumns ? /*#__PURE__*/React.createElement(IssueLikeDataTableView, _extends({
176
212
  testId: 'datasource-table-view',
177
213
  hasNextPage: hasNextPage,
178
214
  items: responseItems,
@@ -184,12 +220,16 @@ const DatasourceTableViewWithoutAnalytics = ({
184
220
  visibleColumnKeys: hasStaleVisibleColumnKeys ? defaultVisibleColumnKeys : visibleColumnKeys || defaultVisibleColumnKeys,
185
221
  onVisibleColumnKeysChange: onVisibleColumnKeysChange,
186
222
  columnCustomSizes: columnCustomSizes,
187
- onColumnResize: onColumnResize,
223
+ onColumnResize: onColumnResize
224
+ }, shouldEnableColumnSort && fg('platform_lp_jira_sllv_renderer_column_sorting') ? {
225
+ onColumnSort,
226
+ sortState
227
+ } : {}, {
188
228
  wrappedColumnKeys: wrappedColumnKeys,
189
229
  onWrappedColumnChange: onWrappedColumnChange,
190
230
  scrollableContainerHeight: isInPDFRender ? undefined : fg('lp_enable_datasource-table-view_height_override') ? scrollableContainerHeight : DefaultScrollableContainerHeight,
191
231
  extensionKey: extensionKey
192
- }) : /*#__PURE__*/React.createElement(EmptyState, {
232
+ })) : /*#__PURE__*/React.createElement(EmptyState, {
193
233
  testId: "datasource-table-view-skeleton",
194
234
  isCompact: true
195
235
  }), /*#__PURE__*/React.createElement(TableFooter, {
@@ -14,6 +14,7 @@
14
14
  .pm-table-wrapper>table thead ._1rmlidpf:last-of-type{border:0}
15
15
  .pm-table-wrapper>table thead ._1u3bidpf{border:0}
16
16
  .pm-table-wrapper>table thead ._ex0g13hi:last-of-type{background:linear-gradient(90deg,hsla(0,0%,100%,0) 0,var(--ds-elevation-surface-current,#fff) 10%)}
17
+ ._1wtnze3t button{padding-block:var(--ds-space-0,0)}
17
18
  .ProseMirror .pm-table-wrapper>table tbody ._15lria51{border-bottom:var(--ds-border-width,1px) solid var(--ds-border,#0b120e24)}
18
19
  .ProseMirror .pm-table-wrapper>table tbody ._1ho1idpf:last-of-type{border-right:0}
19
20
  .ProseMirror .pm-table-wrapper>table tbody ._1xqpia51{border-right:var(--ds-border-width,1px) solid var(--ds-border,#0b120e24)}
@@ -66,6 +67,7 @@ thead.has-column-picker ._10i2idpf:nth-last-of-type(2){border-right:0}.ProseMirr
66
67
  .ProseMirror .pm-table-wrapper>table thead ._zjk41if8:last-of-type{position:sticky}
67
68
  ._11681tcg [data-testid=datasource-header-content--container]{line-height:24px}
68
69
  ._11j11b66 [data-testid=datasource-header-content--container]{padding-right:var(--ds-space-050,4px)}
70
+ ._11lvze3t button{padding-left:var(--ds-space-0,0)}
69
71
  ._124in7od [data-testid=inline-card-icon-and-title]{white-space:unset}
70
72
  ._12pn15vq [data-testid=datasource-header-content--container]{overflow-x:hidden}
71
73
  ._12ruusvi:last-of-type{box-sizing:border-box}
@@ -108,7 +110,9 @@ thead.has-column-picker ._10i2idpf:nth-last-of-type(2){border-right:0}.ProseMirr
108
110
  ._btyzidpf{border-spacing:0}
109
111
  ._ca0qv77o{padding-top:var(--ds-space-025,2px)}
110
112
  ._ca0qze3t{padding-top:var(--ds-space-0,0)}
113
+ ._ficf1e5h button{text-align:left}
111
114
  ._iscccj1k [data-testid=datasource-header-content--container]{display:-webkit-box}
115
+ ._jq8g1wug button{height:auto}
112
116
  ._k48p1wq8{font-weight:var(--ds-font-weight-medium,500)}
113
117
  ._kqsw1if8{position:sticky}
114
118
  ._kqswh2mm{position:relative}
@@ -130,6 +134,7 @@ thead.has-column-picker ._10i2idpf:nth-last-of-type(2){border-right:0}.ProseMirr
130
134
  ._vchh1ntv{box-sizing:content-box}
131
135
  ._vchhusvi{box-sizing:border-box}
132
136
  ._vwz41tcg{line-height:24px}
137
+ ._yhjm1b66 button{padding-right:var(--ds-space-050,4px)}
133
138
  ._yq5hus1c{border-collapse:separate}
134
139
  .pm-table-wrapper>table tbody ._18a21kw7{vertical-align:inherit}
135
140
  .pm-table-wrapper>table tbody ._1h12ze3t{padding-right:var(--ds-space-0,0)}
@@ -7,9 +7,14 @@ import { ax, ix } from "@compiled/react/runtime";
7
7
  /* eslint-disable @atlaskit/design-system/use-tokens-typography */
8
8
 
9
9
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
10
+ import { useIntl } from 'react-intl';
10
11
  import invariant from 'tiny-invariant';
12
+ import Button from '@atlaskit/button/new';
11
13
  import { FlagsProvider } from '@atlaskit/flag';
14
+ import SortAscendingIcon from '@atlaskit/icon/core/sort-ascending';
15
+ import SortDescendingIcon from '@atlaskit/icon/core/sort-descending';
12
16
  import { Skeleton } from '@atlaskit/linking-common';
17
+ import { fg } from '@atlaskit/platform-feature-flags';
13
18
  import { extractClosestEdge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
14
19
  import { reorderWithEdge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/util/reorder-with-edge';
15
20
  import { autoScroller } from '@atlaskit/pragmatic-drag-and-drop-react-beautiful-dnd-autoscroll';
@@ -21,9 +26,11 @@ import { startUfoExperience, succeedUfoExperience } from '../../analytics/ufoExp
21
26
  import { useDatasourceExperienceId } from '../../contexts/datasource-experience-id';
22
27
  import { useIsInPDFRender } from '../../hooks/useIsInPDFRender';
23
28
  import { ColumnPicker } from './column-picker';
29
+ import { GlyphPlaceholder } from './custom-icons';
24
30
  import { DragColumnPreview } from './drag-column-preview';
25
31
  import { DraggableTableHeading } from './draggable-table-heading';
26
32
  import TableEmptyState from './empty-state';
33
+ import { issueLikeTableMessages } from './messages';
27
34
  import { renderType } from './render-type';
28
35
  import { TableCellContent } from './table-cell-content';
29
36
  import { useIsOnScreen } from './useIsOnScreen';
@@ -115,6 +122,22 @@ const tableStyles = null;
115
122
  const noDefaultBorderStyles = null;
116
123
  const headerStyles = null;
117
124
  const headingHoverEffectStyles = null;
125
+
126
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-styled -- Matches DraggableTableHeading button normalization.
127
+ const SortableHeaderParent = forwardRef(({
128
+ as: C = "div",
129
+ style: __cmpls,
130
+ ...__cmplp
131
+ }, __cmplr) => {
132
+ return /*#__PURE__*/React.createElement(C, _extends({}, __cmplp, {
133
+ style: __cmpls,
134
+ ref: __cmplr,
135
+ className: ax(["_1e0c1txw _4cvr1h6o _o5721q9c _1wtnze3t _ficf1e5h _jq8g1wug _11lvze3t _yhjm1b66", __cmplp.className])
136
+ }));
137
+ });
138
+ if (process.env.NODE_ENV !== 'production') {
139
+ SortableHeaderParent.displayName = 'SortableHeaderParent';
140
+ }
118
141
  function extractIndex(data) {
119
142
  const {
120
143
  index
@@ -171,14 +194,19 @@ export const IssueLikeDataTableView = ({
171
194
  onVisibleColumnKeysChange,
172
195
  columnCustomSizes,
173
196
  onColumnResize,
197
+ onColumnSort,
174
198
  wrappedColumnKeys,
175
199
  onWrappedColumnChange,
200
+ sortState,
176
201
  status,
177
202
  hasNextPage,
178
203
  scrollableContainerHeight,
179
204
  extensionKey
180
205
  }) => {
181
206
  var _containerRef$current;
207
+ const {
208
+ formatMessage
209
+ } = useIntl();
182
210
  const tableId = useMemo(() => Symbol('unique-id'), []);
183
211
  const experienceId = useDatasourceExperienceId();
184
212
  const tableHeaderRowRef = useRef(null);
@@ -405,6 +433,9 @@ export const IssueLikeDataTableView = ({
405
433
  }
406
434
  }, [experienceId, extensionKey, hasFullSchema, onLoadDatasourceDetails]);
407
435
  const isEditable = onVisibleColumnKeysChange && hasData;
436
+ // Sorting is enabled only when the table owns the header interaction (read-only mode).
437
+ // Editable tables reserve header interactions for drag/resize/wrap controls.
438
+ const shouldEnableColumnSort = !!onColumnSort && !onVisibleColumnKeysChange && !onColumnResize && !onWrappedColumnChange;
408
439
  const orderedColumnsAreUpToDate = orderedColumns.length === columns.length;
409
440
  const shouldDisplayColumnsInPicker = hasFullSchema && orderedColumnsAreUpToDate;
410
441
  const view = /*#__PURE__*/React.createElement("div", {
@@ -449,7 +480,28 @@ export const IssueLikeDataTableView = ({
449
480
  className: ax(["_11c8wadc _k48p1wq8"])
450
481
  }, content));
451
482
  const isHeadingOutsideButton = !isEditable || !onWrappedColumnChange;
452
- if (isHeadingOutsideButton) {
483
+ if (shouldEnableColumnSort) {
484
+ const sortedDirection = (sortState === null || sortState === void 0 ? void 0 : sortState.key) === key ? sortState.direction : undefined;
485
+ // Use directional icon only for the active sort column; keep width stable otherwise.
486
+ const SortIcon = sortedDirection ? sortedDirection === 'ASC' ? SortAscendingIcon : SortDescendingIcon : GlyphPlaceholder;
487
+ const sortLabel = typeof content === 'string' ? content : key;
488
+ const sortButtonAriaLabel = sortedDirection === 'ASC' ? formatMessage(issueLikeTableMessages.sortByColumnDescendingAction, {
489
+ column: sortLabel
490
+ }) : formatMessage(issueLikeTableMessages.sortByColumnAscendingAction, {
491
+ column: sortLabel
492
+ });
493
+ heading = /*#__PURE__*/React.createElement(SortableHeaderParent, null, /*#__PURE__*/React.createElement(Button, {
494
+ appearance: "subtle",
495
+ spacing: "compact",
496
+ shouldFitContainer: true,
497
+ testId: `${key}-column-sort-button`,
498
+ "aria-label": sortButtonAriaLabel,
499
+ iconAfter: iconProps => /*#__PURE__*/React.createElement(SortIcon, _extends({}, iconProps, {
500
+ size: "small"
501
+ })),
502
+ onClick: () => onColumnSort === null || onColumnSort === void 0 ? void 0 : onColumnSort(key)
503
+ }, heading));
504
+ } else if (isHeadingOutsideButton) {
453
505
  heading = /*#__PURE__*/React.createElement("div", {
454
506
  className: ax(["_1e0c1txw _4cvr1h6o _o5721q9c _8vu418qm _irr3l4ek"])
455
507
  }, heading);
@@ -483,16 +535,18 @@ export const IssueLikeDataTableView = ({
483
535
  onIsWrappedChange: onWrappedColumnChange === null || onWrappedColumnChange === void 0 ? void 0 : onWrappedColumnChange.bind(null, key)
484
536
  }, heading);
485
537
  } else {
486
- return /*#__PURE__*/React.createElement(TableHeading, {
538
+ return /*#__PURE__*/React.createElement(TableHeading, _extends({
487
539
  key: key,
488
540
  "data-testid": `${key}-column-heading`
541
+ }, fg('platform_lp_jira_sllv_renderer_column_sorting') ? {
542
+ 'aria-sort': (sortState === null || sortState === void 0 ? void 0 : sortState.key) === key && sortState.direction === 'ASC' ? 'ascending' : (sortState === null || sortState === void 0 ? void 0 : sortState.key) === key && sortState.direction === 'DESC' ? 'descending' : undefined
543
+ } : {}, {
489
544
  // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
490
- ,
491
545
  style: getWidthCss({
492
546
  shouldUseWidth,
493
547
  width
494
548
  })
495
- }, heading);
549
+ }), heading);
496
550
  }
497
551
  }), onVisibleColumnKeysChange && /*#__PURE__*/React.createElement(ColumnPickerHeader, null, /*#__PURE__*/React.createElement(ColumnPicker, {
498
552
  columns: shouldDisplayColumnsInPicker ? orderedColumns : [],
@@ -40,6 +40,16 @@ export const issueLikeTableMessages = defineMessages({
40
40
  description: 'Table header Dropdown item for making whole column to not wrap text',
41
41
  defaultMessage: 'Unwrap text'
42
42
  },
43
+ sortByColumnAscendingAction: {
44
+ id: 'linkDataSource.issue-line-table.sort-by-column-ascending-action',
45
+ description: 'Accessible label for sorting a table column in ascending order',
46
+ defaultMessage: 'Sort by {column} ascending.'
47
+ },
48
+ sortByColumnDescendingAction: {
49
+ id: 'linkDataSource.issue-line-table.sort-by-column-descending-action',
50
+ description: 'Accessible label for sorting a table column in descending order',
51
+ defaultMessage: 'Sort by {column} descending.'
52
+ },
43
53
  fetchActionErrorGenericDescriptionGalaxia: {
44
54
  id: 'linkDataSource.issue-line-table.fetch-action-error-generic-description-galaxia',
45
55
  description: 'Generic error message description shown when fetching inline edit dropdown field fails',
@@ -0,0 +1,44 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
+ import { JIRA_LIST_OF_LINKS_DATASOURCE_ID } from '../jira-issues-modal';
5
+ var getNextSortDirection = function getNextSortDirection(columnKey, currentSort) {
6
+ if ((currentSort === null || currentSort === void 0 ? void 0 : currentSort.key) === columnKey) {
7
+ return currentSort.direction === 'ASC' ? 'DESC' : 'ASC';
8
+ }
9
+ return 'ASC';
10
+ };
11
+ // Match and remove an existing trailing ORDER BY clause before appending the next sort:
12
+ // - (?:^|\\s+) allows ORDER BY at the start of the JQL or after whitespace.
13
+ // - ORDER\\s+BY matches ORDER BY with flexible spacing and is case-insensitive (/i).
14
+ // - [\\s\\S]*$ consumes everything after ORDER BY to the end of the JQL.
15
+ // Examples:
16
+ // - "project = TEST ORDER BY created DESC" -> "project = TEST"
17
+ // - "ORDER BY priority ASC" -> ""
18
+ var JIRA_ORDER_BY_PATTERN = /(?:^|\s+)ORDER\s+BY\s+[\s\S]*$/i;
19
+ var getJiraColumnSortParameters = function getJiraColumnSortParameters(_ref) {
20
+ var parameters = _ref.parameters,
21
+ columnKey = _ref.columnKey,
22
+ currentSort = _ref.currentSort;
23
+ if (typeof parameters.jql !== 'string') {
24
+ return undefined;
25
+ }
26
+ var direction = getNextSortDirection(columnKey, currentSort);
27
+ var jqlWithoutOrder = parameters.jql.replace(JIRA_ORDER_BY_PATTERN, '').trim();
28
+ var nextOrderByClause = "ORDER BY ".concat(columnKey, " ").concat(direction);
29
+ return {
30
+ parameters: _objectSpread(_objectSpread({}, parameters), {}, {
31
+ jql: "".concat(jqlWithoutOrder, " ").concat(nextOrderByClause)
32
+ }),
33
+ sort: {
34
+ key: columnKey,
35
+ direction: direction
36
+ }
37
+ };
38
+ };
39
+ export var getDatasourceColumnSortGetter = function getDatasourceColumnSortGetter(datasourceId) {
40
+ if (datasourceId === JIRA_LIST_OF_LINKS_DATASOURCE_ID) {
41
+ return getJiraColumnSortParameters;
42
+ }
43
+ return undefined;
44
+ };
@@ -1,8 +1,11 @@
1
1
  /* datasourceTableView.tsx generated by @compiled/babel-plugin v0.39.1 */
2
+ import _extends from "@babel/runtime/helpers/extends";
3
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
4
  import "./datasourceTableView.compiled.css";
3
5
  import * as React from 'react';
4
6
  import { ax, ix } from "@compiled/react/runtime";
5
- import { useCallback, useEffect, useRef } from 'react';
7
+ import { useCallback, useEffect, useRef, useState } from 'react';
8
+ import isEqual from 'lodash/isEqual';
6
9
  import { withAnalyticsContext } from '@atlaskit/analytics-next';
7
10
  import { IntlMessagesProvider } from '@atlaskit/intl-messages-provider';
8
11
  import { fg } from '@atlaskit/platform-feature-flags';
@@ -26,6 +29,7 @@ import { ProviderAuthRequired } from '../common/error-state/provider-auth-requir
26
29
  import { IssueLikeDataTableView } from '../issue-like-table';
27
30
  import EmptyState from '../issue-like-table/empty-state';
28
31
  import { TableFooter } from '../table-footer';
32
+ import { getDatasourceColumnSortGetter } from './datasource-column-sort';
29
33
  var containerStyles = null;
30
34
  var DefaultScrollableContainerHeight = 590;
31
35
  var DatasourceTableViewWithoutAnalytics = function DatasourceTableViewWithoutAnalytics(_ref) {
@@ -40,9 +44,35 @@ var DatasourceTableViewWithoutAnalytics = function DatasourceTableViewWithoutAna
40
44
  onWrappedColumnChange = _ref.onWrappedColumnChange,
41
45
  _ref$scrollableContai = _ref.scrollableContainerHeight,
42
46
  scrollableContainerHeight = _ref$scrollableContai === void 0 ? DefaultScrollableContainerHeight : _ref$scrollableContai;
47
+ // Local copy lets us apply in-session sort mutations without mutating external parameters.
48
+ var _useState = useState(parameters),
49
+ _useState2 = _slicedToArray(_useState, 2),
50
+ sessionParameters = _useState2[0],
51
+ setSessionParameters = _useState2[1];
52
+ // Tracks the external parameters that the current session/sort state was derived from.
53
+ var sessionBaseParametersRef = useRef(parameters);
54
+ var _useState3 = useState(),
55
+ _useState4 = _slicedToArray(_useState3, 2),
56
+ sortState = _useState4[0],
57
+ setSortState = _useState4[1];
58
+ var columnSortGetter = getDatasourceColumnSortGetter(datasourceId);
59
+ // Sorting is only owned by this view when parent callbacks are absent (read-only rendering mode).
60
+ var isReadOnlyDatasourceTable = !onVisibleColumnKeysChange && !onColumnResize && !onWrappedColumnChange;
61
+ // Keep sort UI hidden unless datasource + ownership + feature gate all allow it.
62
+ var shouldEnableColumnSort = !!columnSortGetter && isReadOnlyDatasourceTable && fg('platform_lp_jira_sllv_renderer_column_sorting');
63
+ useDeepEffect(function () {
64
+ // External parameter updates should always reset local sort/session state back to source-of-truth.
65
+ sessionBaseParametersRef.current = parameters;
66
+ setSessionParameters(parameters);
67
+ setSortState(undefined);
68
+ }, [parameters]);
69
+ var isSessionBasedOnCurrentParameters = isEqual(sessionBaseParametersRef.current, parameters);
70
+ // Use session parameters only when they are known to be based on the current external parameters.
71
+ // This avoids a one-render stale read during the deep-effect update cycle above.
72
+ var activeParameters = isSessionBasedOnCurrentParameters && fg('platform_lp_jira_sllv_renderer_column_sorting') ? sessionParameters : parameters;
43
73
  var _useDatasourceTableSt = useDatasourceTableState({
44
74
  datasourceId: datasourceId,
45
- parameters: parameters,
75
+ parameters: activeParameters,
46
76
  fieldKeys: visibleColumnKeys
47
77
  }),
48
78
  reset = _useDatasourceTableSt.reset,
@@ -98,7 +128,7 @@ var DatasourceTableViewWithoutAnalytics = function DatasourceTableViewWithoutAna
98
128
  reset();
99
129
  }
100
130
  isInitialRender.current = false;
101
- }, [reset, parameters]);
131
+ }, [reset, activeParameters]);
102
132
  useEffect(function () {
103
133
  if (onVisibleColumnKeysChange && (visibleColumnKeys || []).length === 0 && defaultVisibleColumnKeys.length > 0) {
104
134
  onVisibleColumnKeysChange(defaultVisibleColumnKeys);
@@ -144,6 +174,19 @@ var DatasourceTableViewWithoutAnalytics = function DatasourceTableViewWithoutAna
144
174
  });
145
175
  forcedReset();
146
176
  }, [destinationObjectTypes, extensionKey, fireEvent, forcedReset]);
177
+ var onColumnSort = useCallback(function (columnKey) {
178
+ // Build next params/sort atomically so UI state and query state stay in sync.
179
+ var result = columnSortGetter === null || columnSortGetter === void 0 ? void 0 : columnSortGetter({
180
+ parameters: sessionParameters,
181
+ columnKey: columnKey,
182
+ currentSort: sortState
183
+ });
184
+ if (!result) {
185
+ return;
186
+ }
187
+ setSortState(result.sort);
188
+ setSessionParameters(result.parameters);
189
+ }, [columnSortGetter, sessionParameters, sortState]);
147
190
  var handleErrorRefresh = useCallback(function () {
148
191
  reset({
149
192
  shouldForceRequest: true
@@ -175,7 +218,7 @@ var DatasourceTableViewWithoutAnalytics = function DatasourceTableViewWithoutAna
175
218
  loaderFn: fetchMessagesForLocale
176
219
  }, /*#__PURE__*/React.createElement("div", {
177
220
  className: ax(["_2rko1kw7", "datasource-table"])
178
- }, hasColumns ? /*#__PURE__*/React.createElement(IssueLikeDataTableView, {
221
+ }, hasColumns ? /*#__PURE__*/React.createElement(IssueLikeDataTableView, _extends({
179
222
  testId: 'datasource-table-view',
180
223
  hasNextPage: hasNextPage,
181
224
  items: responseItems,
@@ -187,12 +230,16 @@ var DatasourceTableViewWithoutAnalytics = function DatasourceTableViewWithoutAna
187
230
  visibleColumnKeys: hasStaleVisibleColumnKeys ? defaultVisibleColumnKeys : visibleColumnKeys || defaultVisibleColumnKeys,
188
231
  onVisibleColumnKeysChange: onVisibleColumnKeysChange,
189
232
  columnCustomSizes: columnCustomSizes,
190
- onColumnResize: onColumnResize,
233
+ onColumnResize: onColumnResize
234
+ }, shouldEnableColumnSort && fg('platform_lp_jira_sllv_renderer_column_sorting') ? {
235
+ onColumnSort: onColumnSort,
236
+ sortState: sortState
237
+ } : {}, {
191
238
  wrappedColumnKeys: wrappedColumnKeys,
192
239
  onWrappedColumnChange: onWrappedColumnChange,
193
240
  scrollableContainerHeight: isInPDFRender ? undefined : fg('lp_enable_datasource-table-view_height_override') ? scrollableContainerHeight : DefaultScrollableContainerHeight,
194
241
  extensionKey: extensionKey
195
- }) : /*#__PURE__*/React.createElement(EmptyState, {
242
+ })) : /*#__PURE__*/React.createElement(EmptyState, {
196
243
  testId: "datasource-table-view-skeleton",
197
244
  isCompact: true
198
245
  }), /*#__PURE__*/React.createElement(TableFooter, {
@@ -14,6 +14,7 @@
14
14
  .pm-table-wrapper>table thead ._1rmlidpf:last-of-type{border:0}
15
15
  .pm-table-wrapper>table thead ._1u3bidpf{border:0}
16
16
  .pm-table-wrapper>table thead ._ex0g13hi:last-of-type{background:linear-gradient(90deg,hsla(0,0%,100%,0) 0,var(--ds-elevation-surface-current,#fff) 10%)}
17
+ ._1wtnze3t button{padding-block:var(--ds-space-0,0)}
17
18
  .ProseMirror .pm-table-wrapper>table tbody ._15lria51{border-bottom:var(--ds-border-width,1px) solid var(--ds-border,#0b120e24)}
18
19
  .ProseMirror .pm-table-wrapper>table tbody ._1ho1idpf:last-of-type{border-right:0}
19
20
  .ProseMirror .pm-table-wrapper>table tbody ._1xqpia51{border-right:var(--ds-border-width,1px) solid var(--ds-border,#0b120e24)}
@@ -66,6 +67,7 @@ thead.has-column-picker ._10i2idpf:nth-last-of-type(2){border-right:0}.ProseMirr
66
67
  .ProseMirror .pm-table-wrapper>table thead ._zjk41if8:last-of-type{position:sticky}
67
68
  ._11681tcg [data-testid=datasource-header-content--container]{line-height:24px}
68
69
  ._11j11b66 [data-testid=datasource-header-content--container]{padding-right:var(--ds-space-050,4px)}
70
+ ._11lvze3t button{padding-left:var(--ds-space-0,0)}
69
71
  ._124in7od [data-testid=inline-card-icon-and-title]{white-space:unset}
70
72
  ._12pn15vq [data-testid=datasource-header-content--container]{overflow-x:hidden}
71
73
  ._12ruusvi:last-of-type{box-sizing:border-box}
@@ -108,7 +110,9 @@ thead.has-column-picker ._10i2idpf:nth-last-of-type(2){border-right:0}.ProseMirr
108
110
  ._btyzidpf{border-spacing:0}
109
111
  ._ca0qv77o{padding-top:var(--ds-space-025,2px)}
110
112
  ._ca0qze3t{padding-top:var(--ds-space-0,0)}
113
+ ._ficf1e5h button{text-align:left}
111
114
  ._iscccj1k [data-testid=datasource-header-content--container]{display:-webkit-box}
115
+ ._jq8g1wug button{height:auto}
112
116
  ._k48p1wq8{font-weight:var(--ds-font-weight-medium,500)}
113
117
  ._kqsw1if8{position:sticky}
114
118
  ._kqswh2mm{position:relative}
@@ -130,6 +134,7 @@ thead.has-column-picker ._10i2idpf:nth-last-of-type(2){border-right:0}.ProseMirr
130
134
  ._vchh1ntv{box-sizing:content-box}
131
135
  ._vchhusvi{box-sizing:border-box}
132
136
  ._vwz41tcg{line-height:24px}
137
+ ._yhjm1b66 button{padding-right:var(--ds-space-050,4px)}
133
138
  ._yq5hus1c{border-collapse:separate}
134
139
  .pm-table-wrapper>table tbody ._18a21kw7{vertical-align:inherit}
135
140
  .pm-table-wrapper>table tbody ._1h12ze3t{padding-right:var(--ds-space-0,0)}