lightning-base-components 1.13.10-alpha → 1.14.4-alpha

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 (69) hide show
  1. package/metadata/raptor.json +24 -0
  2. package/package.json +20 -4
  3. package/scopedImports/@salesforce-internal-core.appVersion.js +1 -1
  4. package/scopedImports/@salesforce-label-LightningDualListbox.movedOptionsPlural.js +1 -0
  5. package/scopedImports/@salesforce-label-LightningDualListbox.movedOptionsSingular.js +1 -0
  6. package/scopedImports/@salesforce-label-LightningErrorMessage.validitySelectAtleastOne.js +1 -0
  7. package/scopedImports/@salesforce-label-LightningMap.titleWithAddress.js +1 -0
  8. package/scopedImports/@salesforce-label-LightningModalBase.cancelandclose.js +1 -0
  9. package/src/lightning/ariaObserver/__component__/ariaObserver.spec.js +112 -0
  10. package/src/lightning/ariaObserver/__docs__/ariaObserver.md +142 -0
  11. package/src/lightning/{utilsPrivate/contentMutation.js → ariaObserver/ariaObserver.js} +60 -98
  12. package/src/lightning/buttonMenu/keyboard.js +0 -10
  13. package/src/lightning/card/card.html +6 -0
  14. package/src/lightning/checkboxGroup/checkboxGroup.html +2 -2
  15. package/src/lightning/checkboxGroup/checkboxGroup.js +6 -1
  16. package/src/lightning/colorPickerCustom/colorPickerCustom.js +20 -1
  17. package/src/lightning/datatable/__docs__/datatable.md +55 -0
  18. package/src/lightning/datatable/__examples__/basic/basic.html +1 -1
  19. package/src/lightning/datatable/columns-shared.js +1 -1
  20. package/src/lightning/datatable/datatable.js +98 -30
  21. package/src/lightning/datatable/errors.js +20 -9
  22. package/src/lightning/datatable/headerActions.js +77 -49
  23. package/src/lightning/datatable/infiniteLoading.js +100 -28
  24. package/src/lightning/datatable/inlineEdit.js +505 -379
  25. package/src/lightning/datatable/inlineEditShared.js +24 -0
  26. package/src/lightning/datatable/keyboard.js +162 -127
  27. package/src/lightning/datatable/renderManager.js +201 -133
  28. package/src/lightning/datatable/rowLevelActions.js +17 -13
  29. package/src/lightning/datatable/rowNumber.js +54 -20
  30. package/src/lightning/datatable/rowSelection.js +760 -0
  31. package/src/lightning/datatable/rowSelectionShared.js +79 -0
  32. package/src/lightning/datatable/rows.js +17 -6
  33. package/src/lightning/datatable/state.js +16 -2
  34. package/src/lightning/datatable/templates/div/div.css +4 -0
  35. package/src/lightning/datatable/templates/div/div.html +6 -0
  36. package/src/lightning/datatable/templates/table/table.html +5 -0
  37. package/src/lightning/datatable/utils.js +14 -0
  38. package/src/lightning/datatable/wrapText.js +77 -47
  39. package/src/lightning/dualListbox/dualListbox.html +1 -1
  40. package/src/lightning/dualListbox/dualListbox.js +42 -0
  41. package/src/lightning/formattedDateTime/__docs__/formattedDateTime.md +36 -3
  42. package/src/lightning/formattedDateTime/__examples__/datetime/datetime.html +2 -2
  43. package/src/lightning/formattedDateTime/__examples__/datetime/datetime.js +3 -1
  44. package/src/lightning/formattedDateTime/__examples__/time/time.html +1 -1
  45. package/src/lightning/formattedDateTime/__examples__/time/time.js +3 -1
  46. package/src/lightning/formattedDateTime/formattedDateTime.js +1 -0
  47. package/src/lightning/input/input.html +1 -5
  48. package/src/lightning/input/input.js +69 -48
  49. package/src/lightning/inputUtils/validity.js +12 -1
  50. package/src/lightning/pillContainer/__docs__/pillContainer.md +45 -1
  51. package/src/lightning/primitiveCellActions/primitiveCellActions.js +69 -12
  52. package/src/lightning/primitiveCellFactory/cellWithStandardLayout.html +13 -11
  53. package/src/lightning/primitiveCellFactory/primitiveCellFactory.js +13 -8
  54. package/src/lightning/primitiveDatatableIeditPanel/primitiveDatatableIeditPanel.html +17 -14
  55. package/src/lightning/primitiveDatatableIeditPanel/primitiveDatatableIeditPanel.js +167 -98
  56. package/src/lightning/primitiveDatatableIeditTypeFactory/primitiveDatatableIeditTypeFactory.js +94 -69
  57. package/src/lightning/primitiveDatatableStatusBar/primitiveDatatableStatusBar.html +4 -4
  58. package/src/lightning/primitiveDatatableStatusBar/primitiveDatatableStatusBar.js +4 -4
  59. package/src/lightning/primitiveHeaderActions/primitiveHeaderActions.js +99 -37
  60. package/src/lightning/progressIndicator/progressIndicator.js +1 -1
  61. package/src/lightning/progressStep/progressStep.js +30 -22
  62. package/src/lightning/staticMap/staticMap.html +1 -0
  63. package/src/lightning/staticMap/staticMap.js +39 -2
  64. package/src/lightning/utils/classSet.js +4 -1
  65. package/src/lightning/utilsPrivate/utilsPrivate.js +12 -1
  66. package/scopedImports/@salesforce-label-LightningModalBase.close.js +0 -1
  67. package/src/lightning/datatable/inlineEdit-shared.js +0 -14
  68. package/src/lightning/datatable/selector-shared.js +0 -38
  69. package/src/lightning/datatable/selector.js +0 -527
@@ -0,0 +1,79 @@
1
+ /**
2
+ * This file exists in order to get around circular dependencies.
3
+ * In this case, rowSelection.js has a dependency on rows.js;
4
+ * but rows.js also has a dependency on rowSelection.js for
5
+ * `isSelectedRow()` among others.
6
+ *
7
+ * We split out the functions that could cause circular dependencies with
8
+ * `rowSelection.js` into the `*Shared.js` files.
9
+ */
10
+
11
+ /**
12
+ * Returns whether or not the row is selected using the state and the rowKeyValue
13
+ * The state maintains the list of selected rows from which
14
+ * this value can be retrieved.
15
+ *
16
+ * @param {Object} state - datatable state object
17
+ * @param {String} rowKeyValue
18
+ * @returns
19
+ */
20
+ export function isSelectedRow(state, rowKeyValue) {
21
+ return !!state.selectedRowsKeys[rowKeyValue];
22
+ }
23
+
24
+ /**
25
+ * Returns whether the row (whose row key value is specified)
26
+ * should be disabled or not.
27
+ * Should not disable if the row is selected.
28
+ * If the particular row is not selected, the row should be disabled
29
+ * when max-row-selection > 1 and the selection limit is reached
30
+ *
31
+ * Note: Do not disable selection when max-row-selection = 1 and
32
+ * a row has been selected.
33
+ *
34
+ * @param {Object} state
35
+ * @param {String} rowKeyValue
36
+ * @returns {Boolean} whether the row should be disabled or not
37
+ */
38
+ export function isDisabledRow(state, rowKeyValue) {
39
+ if (!isSelectedRow(state, rowKeyValue)) {
40
+ const maxRowSelection = getMaxRowSelection(state);
41
+
42
+ // when selection is 1, we should not disable selection
43
+ return (
44
+ maxRowSelection !== 1 &&
45
+ getCurrentSelectionLength(state) === maxRowSelection
46
+ );
47
+ }
48
+
49
+ return false;
50
+ }
51
+
52
+ /**
53
+ * Returns which input type to use for row selection.
54
+ * If max-row-selection = 1, use radio buttons,
55
+ * otherwise, use checkboxes.
56
+ *
57
+ * @param {Object} state - datatable's state object
58
+ * @returns
59
+ */
60
+ export function getRowSelectionInputType(state) {
61
+ if (getMaxRowSelection(state) === 1) {
62
+ return 'radio';
63
+ }
64
+ return 'checkbox';
65
+ }
66
+
67
+ export function getMaxRowSelection(state) {
68
+ return state.maxRowSelection;
69
+ }
70
+
71
+ export function getCurrentSelectionLength(state) {
72
+ return getSelectedRowsKeys(state).length;
73
+ }
74
+
75
+ export function getSelectedRowsKeys(state) {
76
+ return Object.keys(state.selectedRowsKeys).filter(
77
+ (key) => state.selectedRowsKeys[key]
78
+ );
79
+ }
@@ -1,11 +1,11 @@
1
1
  import { assert } from 'lightning/utilsPrivate';
2
- import { classSet, isObjectLike } from './utils';
2
+ import { classSet, isObjectLike, styleToString } from './utils';
3
3
  import { isTreeType, getAttributesNames } from './types';
4
4
  import {
5
5
  isSelectedRow,
6
6
  isDisabledRow,
7
7
  getRowSelectionInputType,
8
- } from './selector-shared';
8
+ } from './rowSelectionShared';
9
9
  import { getTreeStateIndicatorFieldNames, getStateTreeColumn } from './tree';
10
10
  import {
11
11
  getColumns,
@@ -17,7 +17,8 @@ import {
17
17
  } from './columns';
18
18
  import { isRowNumberColumn, getRowNumberErrorColumnDef } from './rowNumber';
19
19
  import { getRowError } from './errors';
20
- import { getDirtyValue } from './inlineEdit-shared';
20
+ import { getDirtyValueFromCell } from './inlineEditShared';
21
+ import { DEFAULT_ROW_HEIGHT } from './renderManager';
21
22
 
22
23
  export function getData(state) {
23
24
  return state.data;
@@ -115,7 +116,7 @@ export function uniqueRowKeyGenerator(keyField) {
115
116
  */
116
117
  export function updateRowsAndCellIndexes() {
117
118
  const { state, privateTypes: types } = this;
118
- const { keyField, renderModeRoleBased } = state;
119
+ const { keyField, renderModeRoleBased, virtualize } = state;
119
120
  const data = getData(state);
120
121
  const columns = getColumns(state);
121
122
  const computeUniqueRowKey = uniqueRowKeyGenerator(keyField);
@@ -141,11 +142,21 @@ export function updateRowsAndCellIndexes() {
141
142
  row.classnames = resolveRowClassNames(row);
142
143
  Object.assign(row, getRowStateForTree(rowData, state));
143
144
  row.tabIndex = -1;
145
+ if (virtualize) {
146
+ row.style = styleToString({
147
+ position: 'absolute',
148
+ top: `${rowIndex * DEFAULT_ROW_HEIGHT}px`,
149
+ });
150
+ }
144
151
 
145
152
  columns.reduce((currentRow, colData, colIndex) => {
146
153
  const { fieldName } = colData;
147
154
  const colKeyValue = generateColKeyValue(colData, colIndex);
148
- const dirtyValue = getDirtyValue(state, row.key, colKeyValue);
155
+ const dirtyValue = getDirtyValueFromCell(
156
+ state,
157
+ row.key,
158
+ colKeyValue
159
+ );
149
160
  const cellHasErrors = hasCellErrors(
150
161
  rowErrors,
151
162
  colData.fieldName,
@@ -391,7 +402,7 @@ function computeCellEditable(row, column) {
391
402
  * @param {object} row - a row data object stored in datatable state. Must be truthy.
392
403
  * @param {object} column - a column data object stored in datatable state. Must be truthy.
393
404
  */
394
- function isCellEditable(row, column) {
405
+ export function isCellEditable(row, column) {
395
406
  return !!resolveAttributeValue(column.editable, row);
396
407
  }
397
408
 
@@ -17,8 +17,9 @@ export const getDefaultState = function () {
17
17
  rows: [],
18
18
  indexes: {},
19
19
 
20
- // selector
20
+ // row selection
21
21
  selectedRowsKeys: {},
22
+ lastSelectedRowKey: undefined,
22
23
  maxRowSelection: undefined,
23
24
 
24
25
  headerIndexes: {},
@@ -54,12 +55,25 @@ export const getDefaultState = function () {
54
55
  // table render mode
55
56
  renderModeRoleBased: false,
56
57
 
57
- // viewport rendering
58
+ // viewport rendering and virtualization
58
59
  enableViewportRendering: undefined,
60
+ virtualize: '',
61
+ bufferSize: 5, // number of extra rows rendered on each side outside of viewport
62
+ rowHeight: 30.5,
63
+ renderedRowCount: 0,
64
+ firstVisibleIndex: 0, // first row that should be visible in viewport
59
65
 
60
66
  // inline edit
61
67
  inlineEdit: {
68
+ rowKeyValue: undefined,
69
+ colKeyValue: undefined,
70
+ columnDef: {},
62
71
  dirtyValues: {},
72
+ editedValue: undefined,
73
+ isPanelVisible: false,
74
+ massEditEnabled: false,
75
+ massEditSelectedRows: undefined,
76
+ resolvedAttributeTypes: {},
63
77
  },
64
78
 
65
79
  // errors
@@ -22,6 +22,10 @@
22
22
  border-top: 1px solid #dddbda;
23
23
  }
24
24
 
25
+ .slds-table_header-fixed [data-row-key-value]:first-child {
26
+ border-top: 0;
27
+ }
28
+
25
29
  /* Required for row numbers */
26
30
  [role="row"] {
27
31
  counter-increment: row-number;
@@ -48,6 +48,7 @@
48
48
  <div class="slds-line-height_reset table-header"
49
49
  role="row"
50
50
  aria-rowindex="1"
51
+ data-row-key-value="HEADER"
51
52
  onprivateresizestart={handleResizeStart}
52
53
  onprivateresizeend={handleResizeEnd}>
53
54
  <template for:each={state.columns} for:item="def" for:index="colIndex">
@@ -58,6 +59,7 @@
58
59
  tabindex={def.tabIndex}
59
60
  aria-label={def.ariaLabel}
60
61
  aria-sort={def.sortAriaLabel}
62
+ data-col-key-value={def.colKeyValue}
61
63
  key={def.colKeyValue}>
62
64
 
63
65
  <template if:true={def.fixedWidth}>
@@ -120,6 +122,7 @@
120
122
  aria-setsize={row.setSize}
121
123
  aria-posinset={row.posInSet}
122
124
  aria-rowindex={row.ariaRowIndex}
125
+ style={row.style}
123
126
  tabindex={row.tabIndex}>
124
127
  <template for:each={row.cells} for:item="cell">
125
128
  <template if:true={cell.isCheckbox}>
@@ -129,6 +132,7 @@
129
132
  role="gridcell"
130
133
  tabindex={cell.tabIndex}
131
134
  data-label={cell.dataLabel}
135
+ data-col-key-value={cell.colKeyValue}
132
136
  key={cell.colKeyValue}>
133
137
  <lightning-primitive-cell-checkbox
134
138
  dt-context-id={_datatableId}
@@ -154,6 +158,7 @@
154
158
  aria-readonly={cell.ariaReadOnly}
155
159
  tabindex={cell.tabIndex}
156
160
  data-label={cell.dataLabel}
161
+ data-col-key-value={cell.colKeyValue}
157
162
  key={cell.colKeyValue}>
158
163
  <lightning-primitive-cell-factory
159
164
  types={privateTypes}
@@ -202,6 +207,7 @@
202
207
  aria-readonly={cell.ariaReadOnly}
203
208
  tabindex={cell.tabIndex}
204
209
  data-label={cell.dataLabel}
210
+ data-col-key-value={cell.colKeyValue}
205
211
  key={cell.colKeyValue}>
206
212
  <lightning-primitive-cell-factory
207
213
  types={privateTypes}
@@ -41,6 +41,7 @@
41
41
  <thead>
42
42
  <!-- Column Header Row -->
43
43
  <tr class="slds-line-height_reset"
44
+ data-row-key-value="HEADER"
44
45
  onprivateresizestart={handleResizeStart}
45
46
  onprivateresizeend={handleResizeEnd}>
46
47
  <template for:each={state.columns} for:item="def" for:index="colIndex">
@@ -50,6 +51,7 @@
50
51
  tabindex={def.tabIndex}
51
52
  aria-label={def.ariaLabel}
52
53
  aria-sort={def.sortAriaLabel}
54
+ data-col-key-value={def.colKeyValue}
53
55
  key={def.colKeyValue}>
54
56
 
55
57
  <template if:true={def.fixedWidth}>
@@ -117,6 +119,7 @@
117
119
  role="gridcell"
118
120
  tabindex={cell.tabIndex}
119
121
  data-label={cell.dataLabel}
122
+ data-col-key-value={cell.colKeyValue}
120
123
  key={cell.colKeyValue}>
121
124
  <lightning-primitive-cell-checkbox
122
125
  dt-context-id={_datatableId}
@@ -142,6 +145,7 @@
142
145
  scope="row"
143
146
  tabindex={cell.tabIndex}
144
147
  data-label={cell.dataLabel}
148
+ data-col-key-value={cell.colKeyValue}
145
149
  key={cell.colKeyValue}>
146
150
  <lightning-primitive-cell-factory
147
151
  types={privateTypes}
@@ -190,6 +194,7 @@
190
194
  aria-readonly={cell.ariaReadOnly}
191
195
  tabindex={cell.tabIndex}
192
196
  data-label={cell.dataLabel}
197
+ data-col-key-value={cell.colKeyValue}
193
198
  key={cell.colKeyValue}>
194
199
  <lightning-primitive-cell-factory
195
200
  types={privateTypes}
@@ -109,3 +109,17 @@ export function getScrollOffsetFromTableEnd(el) {
109
109
  el.scrollHeight - el.parentNode.scrollTop - el.parentNode.clientHeight
110
110
  );
111
111
  }
112
+
113
+ /**
114
+ * Utility for converting arrays and plain objects to style strings
115
+ * @param {Array|Object} style
116
+ * @returns {string} representing array/object as a string
117
+ */
118
+ export function styleToString(style) {
119
+ if (!Array.isArray(style)) {
120
+ return Object.entries(style)
121
+ .map(([key, value]) => `${key}:${value}`)
122
+ .join(';');
123
+ }
124
+ return style.join(';');
125
+ }
@@ -3,8 +3,10 @@ import labelWrapText from '@salesforce/label/LightningDatatable.wrapText';
3
3
  import { getStateColumnIndex, getColumns } from './columns';
4
4
  import { normalizeBoolean } from 'lightning/utilsPrivate';
5
5
  import { normalizeNumberAttribute } from './utils';
6
+ import { getDefaultState } from './state';
6
7
 
7
- const nonWrapableTypes = [
8
+ const WRAP_TEXT_DEFAULT = false;
9
+ const NON_WRAPPABLE_TYPES = [
8
10
  'action',
9
11
  'boolean',
10
12
  'button',
@@ -13,17 +15,33 @@ const nonWrapableTypes = [
13
15
  'rowNumber',
14
16
  ];
15
17
 
16
- const WRAP_TEXT_DEFAULT = false;
17
-
18
18
  const i18n = {
19
19
  clipText: labelClipText,
20
20
  wrapText: labelWrapText,
21
21
  };
22
22
 
23
- function isWrapableType(type) {
24
- return nonWrapableTypes.indexOf(type) < 0;
23
+ /************************** WRAP TEXT STATE **************************/
24
+
25
+ // Returns a boolean representing whether or not the column should be text wrapped
26
+ export function getWrapTextState(state = getDefaultState(), colKeyValue) {
27
+ return state.wrapText[colKeyValue] || WRAP_TEXT_DEFAULT;
25
28
  }
26
29
 
30
+ // Sets a boolean value in state's wrapText object against the column key value
31
+ // representing whether or not the column is text wrapped
32
+ export function setWrapTextState(state = getDefaultState(), columnDefinition) {
33
+ const { colKeyValue, type, wrapText } = columnDefinition;
34
+
35
+ if (isWrappableType(type)) {
36
+ state.wrapText[colKeyValue] =
37
+ normalizeBoolean(wrapText) || WRAP_TEXT_DEFAULT;
38
+ }
39
+ }
40
+
41
+ /************************** WRAP TEXT MAX LINES **************************/
42
+
43
+ // Normalizes and sets wrapTextMaxLines in datatable's state object
44
+ // The normalized value should be a positive integer or it'll fall back to undefined
27
45
  export function setWrapTextMaxLines(state, value) {
28
46
  state.wrapTextMaxLines = normalizeNumberAttribute(
29
47
  'wrapTextMaxLines',
@@ -33,6 +51,8 @@ export function setWrapTextMaxLines(state, value) {
33
51
  );
34
52
  }
35
53
 
54
+ // Sets the `wrapText` and `wrapTextMaxLines` values in the cell object for all cells in a column
55
+ // These values are used by primitiveCellFactory to set the required classes on the cell for wrapping
36
56
  function updateWrapTextAndMaxLinesValuesInCells(state, colIndex, colKeyValue) {
37
57
  state.rows.forEach((row) => {
38
58
  const cell = row.cells[colIndex];
@@ -43,39 +63,17 @@ function updateWrapTextAndMaxLinesValuesInCells(state, colIndex, colKeyValue) {
43
63
  });
44
64
  }
45
65
 
46
- function updateSelectedOptionInHeaderActions(state, colKeyValue) {
47
- const columns = getColumns(state);
48
- const colIndex = getStateColumnIndex(state, colKeyValue);
49
- const colData = columns[colIndex];
50
-
51
- colData.actions.internalActions.forEach((action) => {
52
- if (action.name === 'wrapText') {
53
- action.checked = state.wrapText[colKeyValue];
54
- }
55
- if (action.name === 'clipText') {
56
- action.checked = !state.wrapText[colKeyValue];
57
- }
58
- });
59
-
60
- updateWrapTextAndMaxLinesValuesInCells(state, colIndex, colKeyValue);
61
-
62
- // lets force a refresh on this column, because the wrapText checked value changed.
63
- colData.actions = Object.assign({}, colData.actions);
64
- }
65
-
66
- export function getWrapTextState(state = getDefaultState(), colKeyValue) {
67
- return state.wrapText[colKeyValue] || WRAP_TEXT_DEFAULT;
68
- }
69
-
70
- export function setWrapTextState(state = getDefaultState(), columnDefinition) {
71
- const { colKeyValue, type, wrapText } = columnDefinition;
72
-
73
- if (isWrapableType(type)) {
74
- state.wrapText[colKeyValue] =
75
- normalizeBoolean(wrapText) || WRAP_TEXT_DEFAULT;
76
- }
77
- }
78
-
66
+ /************************** HEADER ACTIONS **************************/
67
+
68
+ /**
69
+ * Returns an object representing the two internal header actions that datatable
70
+ * provides - Wrap Text and Clip Text.
71
+ * Each header action contains a label, title, action name and its selected value (checked)
72
+ *
73
+ * @param {Object} state - datatable's state object
74
+ * @param {Object} columnDefinition - datatable's column definitions
75
+ * @returns
76
+ */
79
77
  export function getActions(state, columnDefinition) {
80
78
  const wrapTextActions = [];
81
79
  const { hideDefaultActions, type, colKeyValue } = columnDefinition;
@@ -84,7 +82,7 @@ export function getActions(state, columnDefinition) {
84
82
  setWrapTextState(state, columnDefinition);
85
83
 
86
84
  // if not hidden and isWrapable, sets the internal actions
87
- if (isWrapableType(type) && !hideDefaultActions) {
85
+ if (isWrappableType(type) && !hideDefaultActions) {
88
86
  const isTextWrapped = getWrapTextState(state, colKeyValue);
89
87
 
90
88
  wrapTextActions.push({
@@ -105,19 +103,51 @@ export function getActions(state, columnDefinition) {
105
103
  return wrapTextActions;
106
104
  }
107
105
 
106
+ /**
107
+ * If the action is an internal action and if the wrapText value for a column
108
+ * needs to be changed in the state, change it to the new value and update
109
+ * the check mark to represent the currently selected action
110
+ *
111
+ * @param {Object} state - datatable's state object
112
+ * @param {String} action - action that was selected/triggered
113
+ * @param {String} colKeyValue - column key value
114
+ */
108
115
  export function handleTriggeredAction(state, action, colKeyValue) {
109
- if (action.name === 'wrapText' || action.name === 'clipText') {
110
- // If will change state
111
- if (state.wrapText[colKeyValue] !== (action.name === 'wrapText')) {
112
- state.wrapText[colKeyValue] = action.name === 'wrapText';
116
+ const actionName = action.name;
117
+ if (actionName === 'wrapText' || actionName === 'clipText') {
118
+ // If state should be changed
119
+ if (state.wrapText[colKeyValue] !== (actionName === 'wrapText')) {
120
+ state.wrapText[colKeyValue] = actionName === 'wrapText';
113
121
 
114
122
  updateSelectedOptionInHeaderActions(state, colKeyValue);
115
123
  }
116
124
  }
117
125
  }
118
126
 
119
- export function getDefaultState() {
120
- return {
121
- wrapText: {},
122
- };
127
+ // Update the 'checked' value of the each action to show which action is selected
128
+ // and which action is not selected
129
+ function updateSelectedOptionInHeaderActions(state, colKeyValue) {
130
+ const columns = getColumns(state);
131
+ const colIndex = getStateColumnIndex(state, colKeyValue);
132
+ const colData = columns[colIndex];
133
+
134
+ colData.actions.internalActions.forEach((action) => {
135
+ if (action.name === 'wrapText') {
136
+ action.checked = state.wrapText[colKeyValue];
137
+ }
138
+ if (action.name === 'clipText') {
139
+ action.checked = !state.wrapText[colKeyValue];
140
+ }
141
+ });
142
+
143
+ updateWrapTextAndMaxLinesValuesInCells(state, colIndex, colKeyValue);
144
+
145
+ // Force a refresh on this column, because the wrapText checked value changed.
146
+ colData.actions = Object.assign({}, colData.actions);
147
+ }
148
+
149
+ /************************** HELPER FUNCTIONS **************************/
150
+
151
+ function isWrappableType(type) {
152
+ return NON_WRAPPABLE_TYPES.indexOf(type) < 0;
123
153
  }
@@ -9,7 +9,7 @@
9
9
  <lightning-helptext if:true={fieldLevelHelp} content={fieldLevelHelp}></lightning-helptext>
10
10
  <div class="slds-form-element__control">
11
11
  <div class="slds-dueling-list" onfocusin={handleFocus} onfocusout={handleBlur}>
12
- <div class="slds-assistive-text" id="assertive-thing" aria-live="assertive"></div>
12
+ <div class="slds-assistive-text" id="assertive-thing" aria-live="assertive">{_messageToDisplay}</div>
13
13
  <div class="slds-assistive-text" id="keyboard-interacton">{i18n.componentAssistiveText}</div>
14
14
  <div class={computedLeftColumnClass}>
15
15
  <span class="slds-form-element__label" id="source-list-label">{sourceLabel}</span>
@@ -14,6 +14,8 @@ import labelRequiredOptionError from '@salesforce/label/LightningDualListbox.req
14
14
  import labelUpButtonAssistiveText from '@salesforce/label/LightningDualListbox.upButtonAssistiveText';
15
15
  import labelMoveSelectionToAssistiveText from '@salesforce/label/LightningDualListbox.moveSelectionToAssistiveText';
16
16
  import labelLoadingText from '@salesforce/label/LightningCombobox.loadingText';
17
+ import labelMovedOptionsSingular from '@salesforce/label/LightningDualListbox.movedOptionsSingular';
18
+ import labelMovedOptionsPlural from '@salesforce/label/LightningDualListbox.movedOptionsPlural';
17
19
  import { LightningElement, api, track } from 'lwc';
18
20
  import { handleKeyDownOnOption } from './keyboard';
19
21
  import { classSet, formatLabel } from 'lightning/utils';
@@ -47,6 +49,8 @@ const i18n = {
47
49
  upButtonAssistiveText: labelUpButtonAssistiveText,
48
50
  moveSelectionToAssistiveText: labelMoveSelectionToAssistiveText,
49
51
  loadingText: labelLoadingText,
52
+ movedOptionsSingular: labelMovedOptionsSingular,
53
+ movedOptionsPlural: labelMovedOptionsPlural,
50
54
  };
51
55
 
52
56
  /**
@@ -117,6 +121,9 @@ export default class LightningDualListbox extends LightningElement {
117
121
  @track highlightedOptions = [];
118
122
  @track focusableInSource;
119
123
  @track focusableInSelected;
124
+ @track highlightedOptionsLabel = [];
125
+
126
+ _messageToDisplay = '';
120
127
 
121
128
  isFocusOnList = false;
122
129
 
@@ -705,6 +712,8 @@ export default class LightningDualListbox extends LightningElement {
705
712
  );
706
713
  }
707
714
 
715
+ this.movedOptions(addToSelect);
716
+
708
717
  const oldSelectedValues = this._selectedValues;
709
718
  this._selectedValues = newValues;
710
719
  const invalidMove =
@@ -730,6 +739,7 @@ export default class LightningDualListbox extends LightningElement {
730
739
  this.interactingState.leave();
731
740
  this.isFocusOnList = false;
732
741
  this.highlightedOptions = [];
742
+ this.highlightedOptionsLabel = [];
733
743
  this.optionToFocus = null;
734
744
  }
735
745
 
@@ -785,6 +795,7 @@ export default class LightningDualListbox extends LightningElement {
785
795
  const start = all ? options.length : this.lastSelected;
786
796
  let val, select;
787
797
  this.highlightedOptions = [];
798
+ this.highlightedOptionsLabel = [];
788
799
  for (let i = 0; i < options.length; i++) {
789
800
  select = (i - start) * (i - end) <= 0;
790
801
  if (select) {
@@ -889,6 +900,7 @@ export default class LightningDualListbox extends LightningElement {
889
900
  if (this.selectedList !== currentList || !isMultiple) {
890
901
  if (this.selectedList) {
891
902
  this.highlightedOptions = [];
903
+ this.highlightedOptionsLabel = [];
892
904
  this.lastSelected = -1;
893
905
  }
894
906
  this.selectedList = currentList;
@@ -1013,6 +1025,36 @@ export default class LightningDualListbox extends LightningElement {
1013
1025
  }
1014
1026
  if (!isSame) {
1015
1027
  this.highlightedOptions = [];
1028
+ this.highlightedOptionsLabel = [];
1029
+ }
1030
+ }
1031
+
1032
+ movedOptions(addToSelect) {
1033
+ const listName = addToSelect ? this.selectedLabel : this.sourceLabel;
1034
+
1035
+ for (let i = 0; i < this.highlightedOptions.length; i++) {
1036
+ let selectedOption = addToSelect
1037
+ ? this.computedSourceList.filter(
1038
+ (item) => item.value === this.highlightedOptions[i]
1039
+ )
1040
+ : this.computedSelectedList.filter(
1041
+ (item) => item.value === this.highlightedOptions[i]
1042
+ );
1043
+ this.highlightedOptionsLabel.push(selectedOption[0].label);
1044
+ }
1045
+
1046
+ if (this.highlightedOptions.length) {
1047
+ const strToFormat =
1048
+ this.highlightedOptions.length > 1
1049
+ ? i18n.movedOptionsPlural
1050
+ : i18n.movedOptionsSingular;
1051
+ this._messageToDisplay = formatLabel(
1052
+ strToFormat,
1053
+ this.highlightedOptionsLabel.join(', '),
1054
+ listName
1055
+ );
1056
+ } else {
1057
+ this._messageToDisplay = '';
1016
1058
  }
1017
1059
  }
1018
1060
  }
@@ -45,11 +45,11 @@ are specified, the component uses the default date format based on the user's lo
45
45
 
46
46
  The locale determines the order and format of the month, day, and year. For example, the English (United States) locale's date format is Oct 14, 2020 and the French (France) locale's date format is 14 Oct 2020. The locale doesn't determine the time zone. Time zone is a separate setting.
47
47
 
48
- The locale also determines whether to display time as 24-hour time or 12-hour time with AM and PM.
48
+ The locale also determines whether to display time as 24-hour time or 12-hour time with AM and PM. You can override the locale default with the `hour12` attribute. See _Date and Time Display Examples_ for more information.
49
49
 
50
- Specify optional attributes to modify the date and time display, overriding the locale's default formatting.
50
+ Specify optional attributes listed in the Specification tab to modify the date and time display, overriding the locale's default formatting.
51
51
 
52
- In the Component Library's Playground and the Mini-Playground in the Examples tab, the component is limited to the en-US locale.
52
+ In the Mini-Playground in the Examples tab, the component is limited to the en-US locale.
53
53
 
54
54
  #### Time Zone Considerations
55
55
 
@@ -132,6 +132,35 @@ Displays: 1/11/2019, 6:53 PM EST
132
132
  </template>
133
133
  ```
134
134
 
135
+ Displays: 1/11/2019, 18:53 EST
136
+
137
+ ```html
138
+ <template>
139
+ <lightning-formatted-date-time
140
+ value="1547250828000"
141
+ year="numeric"
142
+ month="numeric"
143
+ day="numeric"
144
+ hour="2-digit"
145
+ hour12={ampm}
146
+ minute="2-digit"
147
+ time-zone-name="short"
148
+ time-zone="America/New_York"
149
+ >
150
+ </lightning-formatted-date-time>
151
+ </template>
152
+ ```
153
+
154
+ Set the `hour12` attribute using a variable. If set to `false` or any other string directly, the component interprets its value as true.
155
+
156
+ ```js
157
+ import { LightningElement } from 'lwc';
158
+
159
+ export default class FormattedDateTimeHour12 extends LightningElement {
160
+ ampm = false;
161
+ }
162
+ ```
163
+
135
164
  #### Date and Time Stored in Salesforce
136
165
 
137
166
  Salesforce uses the ISO8601 format `YYYY-MM-DD` to store date fields, which store a date without time, and includes no time zone information.
@@ -186,6 +215,10 @@ Displays: December 03, 2017, 12:00 PM
186
215
  </template>
187
216
  ```
188
217
 
218
+ #### Usage Considerations
219
+
220
+ This component has usage differences from its Aura counterpart. See [Base Components: Aura Vs Lightning Web Components](docs/component-library/documentation/lwc/lwc.migrate_map_aura_lwc_components) in the Lightning Web Components Developer Guide.
221
+
189
222
  #### Source Code
190
223
 
191
224
  `lightning-formatted-date-time` is available in the [Base Components Recipes GitHub repository](https://github.com/salesforce/base-components-recipes#documentation). It's transpiled into the `c` namespace so that you can use it in your own projects.
@@ -4,8 +4,8 @@
4
4
  <p><lightning-formatted-date-time value="1547250828000" year="2-digit" month="short" day="2-digit" hour="numeric"
5
5
  weekday="long" era="short"></lightning-formatted-date-time></p>
6
6
  <p><lightning-formatted-date-time value="1547250828000" year="numeric" month="numeric" day="numeric" hour="2-digit"
7
- minute="2-digit" time-zone-name="short" hour12="true"></lightning-formatted-date-time></p>
7
+ minute="2-digit" time-zone-name="short"></lightning-formatted-date-time></p>
8
8
  <p><lightning-formatted-date-time value="1547250828000" year="numeric" month="numeric" day="numeric" hour="2-digit"
9
- minute="2-digit" time-zone="UTC" time-zone-name="short" hour12="false"></lightning-formatted-date-time></p>
9
+ minute="2-digit" time-zone="UTC" time-zone-name="short" hour12={ampm}></lightning-formatted-date-time></p>
10
10
  </div>
11
11
  </template>
@@ -1,3 +1,5 @@
1
1
  import { LightningElement } from 'lwc';
2
2
 
3
- export default class FormattedDateTimeDateTime extends LightningElement {}
3
+ export default class FormattedDateTimeDateTime extends LightningElement {
4
+ ampm = false;
5
+ }