@skyscanner/backpack-web 9.2.0-beta.3 → 10.0.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 (118) hide show
  1. package/bpk-animate-height/package.json +3 -3
  2. package/bpk-component-accordion/package.json +7 -7
  3. package/bpk-component-aria-live/README.md +2 -2
  4. package/bpk-component-aria-live/{index.js → index.ts} +0 -1
  5. package/bpk-component-aria-live/package.json +9 -10
  6. package/bpk-component-aria-live/src/BpkAriaLive-test.tsx +60 -0
  7. package/bpk-component-aria-live/src/{BpkAriaLive.js → BpkAriaLive.tsx} +20 -26
  8. package/bpk-component-aria-live/src/accessibility-test.tsx +31 -0
  9. package/bpk-component-autosuggest/package.json +4 -4
  10. package/bpk-component-badge/package.json +5 -5
  11. package/bpk-component-banner-alert/package.json +9 -9
  12. package/bpk-component-barchart/package.json +8 -8
  13. package/bpk-component-blockquote/package.json +2 -2
  14. package/bpk-component-breadcrumb/package.json +5 -5
  15. package/bpk-component-breadcrumb/src/BpkBreadcrumbItem.module.scss +2 -2
  16. package/bpk-component-breakpoint/package.json +3 -3
  17. package/bpk-component-button/package.json +3 -3
  18. package/bpk-component-calendar/DOCS.md +1 -7
  19. package/bpk-component-calendar/README.md +0 -10
  20. package/bpk-component-calendar/index.js +0 -2
  21. package/bpk-component-calendar/package.json +9 -9
  22. package/bpk-component-calendar/src/BpkCalendarDate.js +0 -15
  23. package/bpk-component-calendar/src/BpkCalendarDate.module.scss +17 -50
  24. package/bpk-component-calendar/src/BpkCalendarGrid.js +3 -2
  25. package/bpk-component-calendar/src/BpkCalendarGrid.module.scss +5 -5
  26. package/bpk-component-calendar/src/_variables.scss +1 -3
  27. package/bpk-component-card/package.json +4 -4
  28. package/bpk-component-checkbox/package.json +5 -5
  29. package/bpk-component-chip/package.json +5 -5
  30. package/bpk-component-close-button/package.json +4 -4
  31. package/bpk-component-code/package.json +2 -2
  32. package/bpk-component-content-cards/README.md +43 -0
  33. package/bpk-component-content-cards/index.ts +21 -0
  34. package/bpk-component-content-cards/package.json +23 -0
  35. package/bpk-component-content-cards/src/BpkContentCard-test.tsx +88 -0
  36. package/bpk-component-content-cards/src/BpkContentCard.module.scss +87 -0
  37. package/bpk-component-content-cards/src/BpkContentCard.tsx +84 -0
  38. package/bpk-component-content-cards/src/BpkContentCards-test.tsx +126 -0
  39. package/bpk-component-content-cards/src/BpkContentCards.module.css +18 -0
  40. package/bpk-component-content-cards/src/BpkContentCards.module.scss +51 -0
  41. package/bpk-component-content-cards/src/BpkContentCards.tsx +72 -0
  42. package/bpk-component-content-cards/src/accessibility-test.tsx +64 -0
  43. package/bpk-component-content-container/package.json +2 -2
  44. package/bpk-component-datatable/README.md +78 -21
  45. package/bpk-component-datatable/package.json +5 -6
  46. package/bpk-component-datatable/src/BpkDataTable.js +176 -143
  47. package/bpk-component-datatable/src/BpkDataTable.module.scss +10 -14
  48. package/bpk-component-datatable/src/BpkDataTableColumn.js +37 -40
  49. package/bpk-component-datatable/src/BpkDataTableHeader.js +143 -0
  50. package/bpk-component-datatable/src/{BpkDataTableColumn.module.scss → BpkDataTableHeader.module.scss} +12 -11
  51. package/bpk-component-datatable/src/common-types.js +33 -11
  52. package/bpk-component-datatable/src/sort-types.js +6 -0
  53. package/bpk-component-datatable/src/utils.js +66 -9
  54. package/bpk-component-datepicker/package.json +9 -9
  55. package/bpk-component-description-list/package.json +2 -2
  56. package/bpk-component-dialog/package.json +8 -8
  57. package/bpk-component-drawer/package.json +9 -9
  58. package/bpk-component-fieldset/package.json +12 -12
  59. package/bpk-component-flare/package.json +4 -4
  60. package/bpk-component-floating-notification/package.json +5 -5
  61. package/bpk-component-form-validation/package.json +10 -10
  62. package/bpk-component-graphic-promotion/package.json +6 -5
  63. package/bpk-component-graphic-promotion/src/BpkGraphicPromo-test.tsx +3 -2
  64. package/bpk-component-graphic-promotion/src/BpkGraphicPromo.tsx +24 -24
  65. package/bpk-component-grid/package.json +2 -2
  66. package/bpk-component-grid-toggle/package.json +3 -3
  67. package/bpk-component-horizontal-nav/package.json +6 -6
  68. package/bpk-component-icon/package.json +7 -7
  69. package/bpk-component-image/package.json +6 -6
  70. package/bpk-component-infinite-scroll/package.json +6 -6
  71. package/bpk-component-input/package.json +6 -6
  72. package/bpk-component-label/package.json +2 -2
  73. package/bpk-component-link/package.json +4 -4
  74. package/bpk-component-list/README.md +8 -5
  75. package/bpk-component-list/package.json +2 -2
  76. package/bpk-component-list/src/BpkList.js +18 -2
  77. package/bpk-component-loading-button/package.json +6 -6
  78. package/bpk-component-map/package.json +6 -6
  79. package/bpk-component-map/src/BpkIconMarker.module.scss +3 -3
  80. package/bpk-component-map/src/BpkIconMarkerBackground.module.scss +4 -4
  81. package/bpk-component-map/src/BpkPriceMarker.module.scss +17 -17
  82. package/bpk-component-map/src/DefaultLoadingElement.module.scss +1 -1
  83. package/bpk-component-mobile-scroll-container/package.json +3 -3
  84. package/bpk-component-modal/package.json +9 -9
  85. package/bpk-component-navigation-bar/package.json +8 -8
  86. package/bpk-component-nudger/package.json +7 -7
  87. package/bpk-component-overlay/package.json +4 -4
  88. package/bpk-component-pagination/package.json +4 -4
  89. package/bpk-component-panel/package.json +2 -2
  90. package/bpk-component-phone-input/package.json +9 -9
  91. package/bpk-component-popover/package.json +8 -8
  92. package/bpk-component-price/package.json +3 -3
  93. package/bpk-component-progress/package.json +5 -5
  94. package/bpk-component-radio/package.json +4 -4
  95. package/bpk-component-rating/package.json +4 -4
  96. package/bpk-component-rtl-toggle/package.json +2 -2
  97. package/bpk-component-scrollable-calendar/package.json +7 -7
  98. package/bpk-component-scrollable-calendar/src/BpkScrollableCalendarGridList.js +100 -69
  99. package/bpk-component-scrollable-calendar/src/utils.js +24 -1
  100. package/bpk-component-section-list/package.json +6 -6
  101. package/bpk-component-select/package.json +3 -3
  102. package/bpk-component-skip-link/package.json +2 -2
  103. package/bpk-component-slider/package.json +4 -4
  104. package/bpk-component-spinner/package.json +3 -3
  105. package/bpk-component-split-input/package.json +3 -3
  106. package/bpk-component-star-rating/package.json +5 -5
  107. package/bpk-component-switch/package.json +2 -2
  108. package/bpk-component-table/package.json +2 -2
  109. package/bpk-component-text/package.json +2 -2
  110. package/bpk-component-textarea/package.json +4 -4
  111. package/bpk-component-theme-toggle/package.json +5 -5
  112. package/bpk-component-ticket/package.json +4 -4
  113. package/bpk-component-tooltip/package.json +4 -4
  114. package/bpk-scrim-utils/package.json +2 -2
  115. package/bpk-theming/package.json +2 -2
  116. package/package.json +6 -5
  117. package/bpk-component-datatable/src/bpkHeaderRenderer.js +0 -110
  118. package/bpk-component-datatable/src/sorter.js +0 -166
@@ -18,180 +18,213 @@
18
18
  /* @flow strict */
19
19
 
20
20
  import PropTypes from 'prop-types';
21
- import React, { Component } from 'react';
22
- import { Table, AutoSizer } from 'react-virtualized';
23
- import _omit from 'lodash/omit';
21
+ import React, { useMemo, useState } from 'react';
22
+ import { useTable, useSortBy } from 'react-table';
24
23
 
25
24
  import { cssModules } from '../../bpk-react-utils';
26
25
 
27
26
  import STYLES from './BpkDataTable.module.scss';
28
- import BpkDataTableColumn from './BpkDataTableColumn';
29
- import hasChildrenOfType from './hasChildrenOfType';
30
- import makeSorter, { type Sorter } from './sorter';
31
27
  import type { Props } from './common-types';
28
+ import { SORT_DIRECTION_TYPES } from './sort-types';
29
+ import BpkDataTableHeader from './BpkDataTableHeader';
30
+ import { getColumns } from './utils';
32
31
 
33
32
  const getClassName = cssModules(STYLES);
34
- const omittedTableProps = ['rowGetter', 'rowCount', 'onHeaderClick'];
35
33
 
36
- type State<Row> = {
37
- sorter: Sorter<Row>,
38
- rowSelected: ?number,
34
+ const KEYCODES = {
35
+ ENTER: 13,
36
+ SPACEBAR: 32,
39
37
  };
40
38
 
41
- class BpkDataTable<Row> extends Component<Props<Row>, State<Row>> {
42
- static defaultProps = {
43
- ...Table.defaultProps,
44
- width: null,
45
- headerHeight: 60,
46
- rowHeight: 60,
47
- gridStyle: { direction: undefined }, // This is required for rows to automatically respect rtl
48
- defaultColumnSortIndex: 0,
49
- sort: null,
50
- sortBy: null,
51
- sortDirection: null,
52
- };
53
-
54
- constructor(props: Props<Row>) {
55
- super();
56
-
57
- this.state = {
58
- sorter: makeSorter(props),
59
- rowSelected: undefined,
60
- };
61
- }
62
-
63
- UNSAFE_componentWillReceiveProps(nextProps: Props<Row>) {
64
- if (nextProps.rows !== this.props.rows) {
65
- this.state.sorter.propsChange(nextProps);
66
- }
67
- }
68
-
69
- onRowClicked = ({ index }: { index: number }) => {
70
- if (this.state.rowSelected === index) {
71
- this.setState({ rowSelected: undefined });
72
- } else {
73
- this.setState({ rowSelected: index });
74
- }
75
- if (this.props.onRowClick !== undefined) {
76
- this.props.onRowClick(this.state.sorter.getRow(index));
77
- }
39
+ const BpkDataTable = (props: Props) => {
40
+ const {
41
+ children,
42
+ className,
43
+ defaultColumnSortIndex,
44
+ height,
45
+ rows: data,
46
+ sort,
47
+ sortBy,
48
+ sortDirection,
49
+ width,
50
+ ...restOfProps
51
+ } = props;
52
+
53
+ const [rowSelected, updateRowSelected] = useState(undefined);
54
+
55
+ const classNames = getClassName('bpk-data-table', className);
56
+
57
+ const getRowClassNames = (
58
+ consumerClassName: ?string | ?(({ index: number }) => string),
59
+ index: number,
60
+ ) => {
61
+ const rowClassNames = getClassName(
62
+ 'bpk-data-table__row',
63
+ rowSelected === index && 'bpk-data-table__row--selected',
64
+ props.onRowClick !== undefined && 'bpk-data-table__row--clickable',
65
+ index === -1 && 'bpk-data-table__header-row',
66
+ consumerClassName &&
67
+ (typeof consumerClassName === 'function'
68
+ ? consumerClassName({ index })
69
+ : consumerClassName),
70
+ );
71
+ return rowClassNames;
78
72
  };
79
73
 
80
- onHeaderClick = ({
81
- dataKey: sortBy,
82
- event,
83
- }: {
84
- dataKey: string,
85
- event: SyntheticEvent<HTMLDivElement>,
86
- }) => {
87
- const column = React.Children.toArray(this.props.children).find(
88
- (child) => child.props.dataKey === sortBy,
74
+ const headerClassNames = getClassName(
75
+ 'bpk-data-table__row',
76
+ 'bpk-data-table__header-row',
77
+ props.headerClassName,
78
+ );
79
+
80
+ const columns = useMemo(() => getColumns(children), [children]);
81
+
82
+ const { getTableBodyProps, getTableProps, headerGroups, prepareRow, rows } =
83
+ useTable(
84
+ {
85
+ columns,
86
+ data,
87
+ initialState: {
88
+ sortBy: useMemo(
89
+ () => [
90
+ ...(sort
91
+ ? []
92
+ : [
93
+ {
94
+ id: columns[defaultColumnSortIndex].accessor,
95
+ desc:
96
+ columns[defaultColumnSortIndex].defaultSortDirection ===
97
+ 'DESC' || false,
98
+ },
99
+ ]),
100
+ ],
101
+ [defaultColumnSortIndex, columns, sort],
102
+ ),
103
+ },
104
+ },
105
+ useSortBy,
89
106
  );
90
107
 
91
- if (!column) {
92
- return;
93
- }
94
-
95
- if (column.props.disableSort === true) {
96
- return;
108
+ const onRowClicked = (index: number) => {
109
+ if (rowSelected === index) {
110
+ updateRowSelected(undefined);
111
+ } else {
112
+ updateRowSelected(index);
97
113
  }
98
-
99
- // See: https://reactjs.org/docs/legacy-event-pooling.html
100
- const eventTarget = event.target;
101
- if (eventTarget instanceof Element) {
102
- this.setState((prevState) => ({
103
- sorter: prevState.sorter.onHeaderClick(sortBy, eventTarget, column),
104
- }));
114
+ if (props.onRowClick !== undefined) {
115
+ props.onRowClick(rows[index].original);
105
116
  }
106
117
  };
107
118
 
108
- rowClassName = (
109
- consumerClassName: ?string | ?(({ index: number }) => string),
110
- { index }: { index: number },
119
+ const handleKeyboardEvent = (
120
+ event: React.KeyboardEvent<HTMLElement>,
121
+ i: number,
111
122
  ) => {
112
- const classNames = [getClassName('bpk-data-table__row')];
113
- if (this.state.rowSelected === index) {
114
- classNames.push(getClassName('bpk-data-table__row--selected'));
115
- }
116
- if (this.props.onRowClick !== undefined) {
117
- classNames.push(getClassName('bpk-data-table__row--clickable'));
118
- }
119
- if (index === -1) {
120
- classNames.push(getClassName('bpk-data-table__header-row'));
121
- }
122
- if (consumerClassName) {
123
- if (typeof consumerClassName === 'function') {
124
- classNames.push(consumerClassName({ index }));
125
- } else {
126
- classNames.push(consumerClassName);
127
- }
123
+ if (
124
+ event.keyCode === KEYCODES.ENTER ||
125
+ event.keyCode === KEYCODES.SPACEBAR
126
+ ) {
127
+ event.preventDefault();
128
+ onRowClicked(i);
128
129
  }
129
- return classNames;
130
130
  };
131
131
 
132
- renderTable(width: ?number) {
133
- const {
134
- children,
135
- className,
136
- headerClassName,
137
- rowClassName,
138
- sort,
139
- ...restOfProps
140
- } = this.props;
141
-
142
- const classNames = [getClassName('bpk-data-table')];
143
- if (className) {
144
- classNames.push(className);
145
- }
146
-
147
- const headerClassNames = [getClassName('bpk-data-table__header-column')];
148
- if (headerClassName) {
149
- headerClassNames.push(headerClassName);
150
- }
151
-
152
- return (
153
- // $FlowFixMe[cannot-spread-inexact]
154
- <Table
155
- {...restOfProps}
156
- className={classNames.join(' ')}
157
- width={width}
158
- rowCount={this.state.sorter.rowCount}
159
- rowGetter={({ index }) => this.state.sorter.getRow(index)}
160
- headerClassName={headerClassNames.join(' ')}
161
- rowClassName={(row) => this.rowClassName(rowClassName, row)}
162
- onRowClick={this.onRowClicked}
163
- onHeaderClick={this.onHeaderClick}
164
- {...this.state.sorter.sortProps}
165
- >
166
- {React.Children.map(children, BpkDataTableColumn.toColumn)}
167
- </Table>
168
- );
169
- }
170
-
171
- render() {
172
- if (this.props.width !== null) {
173
- return this.renderTable(this.props.width);
174
- }
175
-
176
- return (
177
- <AutoSizer disableHeight>
178
- {({ width }) => this.renderTable(width)}
179
- </AutoSizer>
180
- );
181
- }
182
- }
132
+ return (
133
+ <div
134
+ {...getTableProps({
135
+ style: { width, height },
136
+ className: classNames,
137
+ })}
138
+ {...restOfProps}
139
+ >
140
+ <div>
141
+ {headerGroups.map((headerGroup) => (
142
+ <div
143
+ {...headerGroup.getHeaderGroupProps({
144
+ style: { height: props.headerHeight },
145
+ className: headerClassNames,
146
+ })}
147
+ >
148
+ {headerGroup.headers.map((column) => {
149
+ if (sort && sortBy) {
150
+ if (column.id === sortBy) {
151
+ column.sortType = sort; // eslint-disable-line no-param-reassign
152
+ column.sortDirection = sortDirection; // eslint-disable-line no-param-reassign
153
+ }
154
+ }
155
+ return <BpkDataTableHeader key={column.id} column={column} />;
156
+ })}
157
+ </div>
158
+ ))}
159
+ </div>
160
+ <div {...getTableBodyProps()}>
161
+ {rows.map((row, i) => {
162
+ prepareRow(row);
163
+ const cellClassNames = [getClassName('bpk-data-table__cell')];
164
+ return (
165
+ <div
166
+ onClick={() => onRowClicked(i)}
167
+ role="button"
168
+ onKeyDown={(event) => handleKeyboardEvent(event, i)}
169
+ tabIndex={0}
170
+ {...row.getRowProps({
171
+ style: {
172
+ ...props.rowStyle,
173
+ height: props.rowHeight,
174
+ },
175
+ className: getRowClassNames(props.rowClassName, i),
176
+ })}
177
+ >
178
+ {row.cells.map((cell) => {
179
+ if (cell.column.className) {
180
+ cellClassNames.push(cell.column.className);
181
+ }
182
+ return (
183
+ <div
184
+ {...cell.getCellProps({
185
+ style: {
186
+ width: cell.column.width,
187
+ minWidth: cell.column.minWidth,
188
+ flexGrow: cell.column.flexGrow,
189
+ },
190
+ className: cellClassNames.join(' '),
191
+ })}
192
+ >
193
+ {cell.render('Cell')}
194
+ </div>
195
+ );
196
+ })}
197
+ </div>
198
+ );
199
+ })}
200
+ </div>
201
+ </div>
202
+ );
203
+ };
183
204
 
184
205
  BpkDataTable.propTypes = {
185
- ..._omit(Table.propTypes, omittedTableProps),
186
206
  rows: PropTypes.arrayOf(Object).isRequired,
187
- children: hasChildrenOfType(BpkDataTableColumn),
207
+ children: PropTypes.node.isRequired,
208
+ height: PropTypes.number.isRequired,
188
209
  width: PropTypes.number,
189
210
  headerHeight: PropTypes.number,
211
+ rowHeight: PropTypes.number,
190
212
  className: PropTypes.string,
191
213
  defaultColumnSortIndex: PropTypes.number,
192
214
  sort: PropTypes.func,
193
215
  sortBy: PropTypes.string,
194
- sortDirection: PropTypes.oneOf(['ASC', 'DESC']),
216
+ sortDirection: PropTypes.oneOf(Object.keys(SORT_DIRECTION_TYPES)),
217
+ };
218
+
219
+ BpkDataTable.defaultProps = {
220
+ width: null,
221
+ headerHeight: 60,
222
+ rowHeight: 60,
223
+ className: null,
224
+ defaultColumnSortIndex: 0,
225
+ sort: null,
226
+ sortBy: null,
227
+ sortDirection: SORT_DIRECTION_TYPES.ASC,
195
228
  };
196
229
 
197
230
  export default BpkDataTable;
@@ -41,19 +41,8 @@ $bpk-border-size-datatable: $bpk-border-size-xl * 2;
41
41
  .bpk-data-table {
42
42
  width: 100%;
43
43
 
44
- &__header-column {
45
- display: flex;
46
- height: 100%;
47
- margin: bpk-spacing-base();
48
- flex-direction: row;
49
- align-items: center;
50
- outline-offset: -(4 * $bpk-one-pixel-rem);
51
-
52
- @include bpk-label-2;
53
-
54
- &:hover {
55
- cursor: pointer;
56
- }
44
+ &__header-row {
45
+ overflow: hidden;
57
46
  }
58
47
 
59
48
  &__row {
@@ -67,7 +56,7 @@ $bpk-border-size-datatable: $bpk-border-size-xl * 2;
67
56
 
68
57
  @include bpk-footnote;
69
58
 
70
- &:last-child {
59
+ &:last-child:not(.bpk-data-table__header-row) {
71
60
  border-bottom: $bpk-border-size-sm solid $bpk-color-sky-gray-tint-07;
72
61
  }
73
62
 
@@ -92,4 +81,11 @@ $bpk-border-size-datatable: $bpk-border-size-xl * 2;
92
81
  }
93
82
  }
94
83
  }
84
+
85
+ &__cell {
86
+ margin: bpk-spacing-base();
87
+ text-overflow: ellipsis;
88
+ white-space: nowrap;
89
+ overflow: hidden;
90
+ }
95
91
  }
@@ -17,48 +17,45 @@
17
17
  */
18
18
  /* @flow strict */
19
19
 
20
- import React, { type Element } from 'react';
21
- import { Column } from 'react-virtualized';
22
-
23
- import { cssModules } from '../../bpk-react-utils';
24
-
25
- import bpkHeaderRenderer from './bpkHeaderRenderer';
26
- import STYLES from './BpkDataTableColumn.module.scss';
27
- import { type SortDirectionType } from './sort-types';
28
-
29
- export type BpkDataTableColumnProps = {
30
- className: ?string,
31
- dataKey: string,
32
- disableSort: boolean,
33
- defaultSortDirection: SortDirectionType,
34
- };
35
-
36
- const getClassName = cssModules(STYLES);
37
-
38
- // BpkDataTableColumn is just a props wrapper since Table only accepts Column children
39
- // BpkDataTable uses BpkDataTableColumn.toColumn to convert BpkDataTableColumn to Columns
40
- // eslint-disable-next-line no-unused-vars
41
- const BpkDataTableColumn = (props: BpkDataTableColumnProps) => null;
42
-
43
- BpkDataTableColumn.toColumn = (
44
- bpkDataTableColumn: Element<typeof BpkDataTableColumn>,
45
- key,
46
- ) => {
47
- const { className, ...rest } = bpkDataTableColumn.props;
48
- const classNames = [getClassName('bpk-data-table-column')];
49
-
50
- if (className) {
51
- classNames.push(className);
52
- }
53
-
54
- // $FlowFixMe[cannot-spread-inexact]
55
- return <Column className={classNames.join(' ')} key={key} {...rest} />;
20
+ import React from 'react';
21
+ import PropTypes from 'prop-types';
22
+
23
+ import { SORT_DIRECTION_TYPES } from './sort-types';
24
+ import { type BpkDataTableColumnProps } from './common-types';
25
+
26
+ // BpkDataTableColumn is just a props wrapper
27
+ // to maintain backwards compatibility with the old API of BpkDataTable which takes columns as children
28
+ // The `react-table` library however expects columns as an array of objects
29
+
30
+ const BpkDataTableColumn = (props: BpkDataTableColumnProps) => (
31
+ <div {...props} />
32
+ );
33
+
34
+ BpkDataTableColumn.propTypes = {
35
+ dataKey: PropTypes.string.isRequired,
36
+ width: PropTypes.number.isRequired,
37
+ className: PropTypes.string,
38
+ disableSort: PropTypes.bool,
39
+ defaultSortDirection: PropTypes.oneOf(Object.keys(SORT_DIRECTION_TYPES)),
40
+ flexGrow: PropTypes.number,
41
+ label: PropTypes.string,
42
+ headerRenderer: PropTypes.func,
43
+ headerClassName: PropTypes.string,
44
+ headerStyle: PropTypes.object, // eslint-disable-line react/forbid-prop-types
45
+ cellRenderer: PropTypes.func,
46
+ cellDataGetter: PropTypes.func,
56
47
  };
57
-
58
- BpkDataTableColumn.propTypes = { ...Column.propTypes };
59
48
  BpkDataTableColumn.defaultProps = {
60
- ...Column.defaultProps,
61
- headerRenderer: bpkHeaderRenderer,
49
+ className: null,
50
+ disableSort: false,
51
+ defaultSortDirection: SORT_DIRECTION_TYPES.ASC,
52
+ flexGrow: 0,
53
+ label: null,
54
+ headerRenderer: null,
55
+ headerClassName: null,
56
+ headerStyle: null,
57
+ cellRenderer: null,
58
+ cellDataGetter: null,
62
59
  };
63
60
 
64
61
  export default BpkDataTableColumn;
@@ -0,0 +1,143 @@
1
+ /*
2
+ * Backpack - Skyscanner's Design System
3
+ *
4
+ * Copyright 2016 Skyscanner Ltd
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+ /* @flow strict */
19
+
20
+ import React from 'react';
21
+
22
+ import { cssModules } from '../../bpk-react-utils';
23
+ import BpkSmallArrowDownIcon from '../../bpk-component-icon/sm/arrow-down';
24
+ import BpkSmallArrowUpIcon from '../../bpk-component-icon/sm/arrow-up';
25
+ import { withRtlSupport } from '../../bpk-component-icon';
26
+
27
+ import { SORT_DIRECTION_TYPES } from './sort-types';
28
+ import STYLES from './BpkDataTableHeader.module.scss';
29
+ import { type ColumnType } from './common-types';
30
+
31
+ const DownIcon = withRtlSupport(BpkSmallArrowDownIcon);
32
+ const UpIcon = withRtlSupport(BpkSmallArrowUpIcon);
33
+
34
+ const getClassName = cssModules(STYLES);
35
+
36
+ const KEYCODES = {
37
+ ENTER: 13,
38
+ SPACEBAR: 32,
39
+ };
40
+
41
+ const BpkDataTableHeader = ({ column }: { column: ColumnType }) => {
42
+ const {
43
+ defaultSortDirection,
44
+ disableSortBy,
45
+ flexGrow,
46
+ headerClassName,
47
+ headerStyle,
48
+ isSorted,
49
+ isSortedDesc,
50
+ label,
51
+ minWidth,
52
+ sortDirection,
53
+ width,
54
+ } = column;
55
+
56
+ const onHeaderClick = () => {
57
+ if (disableSortBy === true) {
58
+ return;
59
+ }
60
+ let isDescending;
61
+
62
+ if (isSorted) {
63
+ isDescending = isSortedDesc === false;
64
+ } else if (sortDirection) {
65
+ isDescending = sortDirection === SORT_DIRECTION_TYPES.DESC;
66
+ } else if (defaultSortDirection) {
67
+ isDescending = defaultSortDirection === SORT_DIRECTION_TYPES.DESC;
68
+ } else {
69
+ isDescending = false;
70
+ }
71
+
72
+ column.toggleSortBy(isDescending);
73
+ };
74
+
75
+ const handleKeyboardEvent = (event: React.KeyboardEvent<HTMLElement>) => {
76
+ if (
77
+ event.keyCode === KEYCODES.ENTER ||
78
+ event.keyCode === KEYCODES.SPACEBAR
79
+ ) {
80
+ event.preventDefault();
81
+ onHeaderClick();
82
+ }
83
+ };
84
+
85
+ const headerClassNames = getClassName(
86
+ 'bpk-data-table-column__header',
87
+ headerClassName,
88
+ );
89
+ const upIconClassNames = getClassName(
90
+ 'bpk-data-table-column__sort-icon--up',
91
+ 'bpk-data-table-column__sort-icon',
92
+ isSorted && !isSortedDesc && 'bpk-data-table-column__sort-icon--selected',
93
+ );
94
+
95
+ const downIconClassNames = getClassName(
96
+ 'bpk-data-table-column__sort-icon--down',
97
+ 'bpk-data-table-column__sort-icon',
98
+ isSorted && isSortedDesc && 'bpk-data-table-column__sort-icon--selected',
99
+ );
100
+
101
+ return (
102
+ <div
103
+ {...column.getHeaderProps({
104
+ style: {
105
+ width,
106
+ minWidth,
107
+ flexGrow,
108
+ ...headerStyle,
109
+ },
110
+ className: headerClassNames,
111
+ })}
112
+ >
113
+ <span
114
+ onClick={onHeaderClick}
115
+ onKeyDown={handleKeyboardEvent}
116
+ aria-label={label}
117
+ aria-pressed={isSorted}
118
+ role="button"
119
+ tabIndex={0}
120
+ >
121
+ {column.render('Header')}
122
+ </span>
123
+ {!disableSortBy && (
124
+ <div
125
+ className={getClassName('bpk-data-table-column__sort-icons')}
126
+ key="sort"
127
+ aria-hidden
128
+ >
129
+ <UpIcon
130
+ onClick={() => column.toggleSortBy(false)}
131
+ className={upIconClassNames}
132
+ />
133
+ <DownIcon
134
+ onClick={() => column.toggleSortBy(true)}
135
+ className={downIconClassNames}
136
+ />
137
+ </div>
138
+ )}
139
+ </div>
140
+ );
141
+ };
142
+
143
+ export default BpkDataTableHeader;
@@ -16,24 +16,25 @@
16
16
  * limitations under the License.
17
17
  */
18
18
 
19
- @import '~bpk-mixins/index';
19
+ @import '~bpk-mixins';
20
20
 
21
21
  $bpk-spacing-v2: true;
22
22
 
23
23
  $bpk-margin-size-sort-icons: bpk-spacing-sm() + bpk-spacing-md();
24
24
 
25
- .bpk-data-table-column {
25
+ .bpk-data-table-column__header {
26
+ display: flex;
27
+ height: 100%;
26
28
  margin: bpk-spacing-base();
27
- text-overflow: ellipsis;
28
- white-space: nowrap;
29
- overflow: hidden;
30
- }
29
+ flex-direction: row;
30
+ align-items: center;
31
+ outline-offset: -(bpk-spacing-sm());
31
32
 
32
- .bpk-data-table-column__header {
33
- display: inline-block;
34
- max-width: 100%;
35
- text-overflow: ellipsis;
36
- overflow: hidden;
33
+ @include bpk-label-2;
34
+
35
+ &:hover {
36
+ cursor: pointer;
37
+ }
37
38
  }
38
39
 
39
40
  .bpk-data-table-column__sort-icons {