@canonical/react-components 0.49.0 → 0.50.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.
@@ -1,6 +1,6 @@
1
1
  import React, { HTMLAttributes, PropsWithChildren, ReactNode } from "react";
2
2
  import "./TablePagination.scss";
3
- export type Props = PropsWithChildren<{
3
+ export type BasePaginationProps = {
4
4
  /**
5
5
  * list of data elements to be paginated. This component is un-opinionated about
6
6
  * the structure of the data but it should be identical to the data structure
@@ -9,7 +9,7 @@ export type Props = PropsWithChildren<{
9
9
  data: unknown[];
10
10
  /**
11
11
  * prop name of the child table component that receives paginated data.
12
- * default value is set to @constant rows, which is the data prop for the @func MainTable component
12
+ * default value is set to `rows`, which is the data prop for the `MainTable` component
13
13
  */
14
14
  dataForwardProp?: string;
15
15
  /**
@@ -32,6 +32,46 @@ export type Props = PropsWithChildren<{
32
32
  * place the pagination component above or below the table?
33
33
  */
34
34
  position?: "above" | "below";
35
- }> & HTMLAttributes<HTMLDivElement>;
36
- declare const TablePagination: ({ data, className, itemName, description, position, dataForwardProp, pageLimits, children, ...divProps }: Props) => React.JSX.Element;
35
+ };
36
+ export type ExternalControlProps = BasePaginationProps & {
37
+ /**
38
+ * Whether the component will be controlled via external state.
39
+ */
40
+ externallyControlled?: true;
41
+ /**
42
+ * the total number of items available within the data. This prop is only relevant
43
+ * and will be required if `externallyControlled` is set to `true`.
44
+ */
45
+ totalItems: number;
46
+ /**
47
+ * the current page that's showing. This prop is only relevant and will be required
48
+ * if `externallyControlled` is set to `true`.
49
+ */
50
+ currentPage: number;
51
+ /**
52
+ * size per page. This prop is only relevant and will be required if
53
+ * `externallyControlled` is set to `true`.
54
+ */
55
+ pageSize: number;
56
+ /**
57
+ * callback indicating a page change event to the parent component.
58
+ * This prop is only relevant and will be required if `externallyControlled` is set
59
+ * to `true`.
60
+ */
61
+ onPageChange: (page: number) => void;
62
+ /**
63
+ * callback indicating a page size change event to the parent component.
64
+ * This prop is only relevant and will be required if `externallyControlled` is set
65
+ * to `true`.
66
+ */
67
+ onPageSizeChange: (pageSize: number) => void;
68
+ };
69
+ export type InternalControlProps = BasePaginationProps & {
70
+ /**
71
+ * Whether the component will be controlled via external state.
72
+ */
73
+ externallyControlled?: false;
74
+ };
75
+ export type Props = PropsWithChildren<ExternalControlProps | InternalControlProps> & HTMLAttributes<HTMLDivElement>;
76
+ declare const TablePagination: (props: Props) => React.JSX.Element;
37
77
  export default TablePagination;
@@ -5,121 +5,85 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
- var _classnames = _interopRequireDefault(require("classnames"));
9
- var _hooks = require("../../hooks");
10
- var _Select = _interopRequireDefault(require("../Select"));
11
- var _TablePaginationControls = _interopRequireDefault(require("./TablePaginationControls"));
12
8
  require("./TablePagination.scss");
9
+ var _TablePaginationControls = _interopRequireDefault(require("./TablePaginationControls"));
10
+ var _utils = require("./utils");
11
+ var _hooks = require("../../hooks");
13
12
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14
13
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
15
14
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
16
15
  function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
17
- /**
18
- * Determine if we are working with a small screen.
19
- * 'small screen' in this case is relative to the width of the description div
20
- */
21
- const figureSmallScreen = descriptionRef => {
22
- const descriptionElement = descriptionRef.current;
23
- if (!descriptionElement) {
24
- return true;
25
- }
26
- return descriptionElement.getBoundingClientRect().width < 230;
27
- };
28
-
29
- /**
30
- * Iterate direct react child components and override the value of the prop specified by @param dataForwardProp
31
- * for those child components.
32
- * @param children - react node children to iterate
33
- * @param dataForwardProp - the name of the prop from the children components to override
34
- * @param data - actual data to be passed to the prop specified by @param dataForwardProp
35
- */
36
- const renderChildren = (children, dataForwardProp, data) => {
37
- return _react.Children.map(children, child => {
38
- return /*#__PURE__*/(0, _react.cloneElement)(child, {
39
- [dataForwardProp]: data
40
- });
41
- });
42
- };
43
- const DEFAULT_PAGE_LIMITS = [50, 100, 200];
44
- const generatePagingOptions = pageLimits => {
45
- return pageLimits.map(limit => ({
46
- value: limit,
47
- label: "".concat(limit, "/page")
48
- }));
49
- };
50
- const TablePagination = _ref => {
51
- let {
16
+ const TablePagination = props => {
17
+ const {
52
18
  data,
53
- className,
19
+ dataForwardProp = "rows",
54
20
  itemName = "item",
21
+ className,
55
22
  description,
23
+ pageLimits = _utils.DEFAULT_PAGE_LIMITS,
56
24
  position = "above",
57
- dataForwardProp = "rows",
58
- pageLimits = DEFAULT_PAGE_LIMITS,
25
+ externallyControlled,
59
26
  children,
60
27
  ...divProps
61
- } = _ref;
62
- const descriptionRef = (0, _react.useRef)(null);
63
- const [isSmallScreen, setSmallScreen] = (0, _react.useState)(false);
64
- const [pageSize, setPageSize] = (0, _react.useState)(() => {
65
- return generatePagingOptions(pageLimits)[0].value;
28
+ } = props;
29
+
30
+ // Safety check to ensure pageSize is a valid option in
31
+ // pageLimits if the component is externally controlled
32
+ if (externallyControlled) {
33
+ let pageSizeFound = false;
34
+ for (const limit of pageLimits) {
35
+ if (limit === Number(props.pageSize)) {
36
+ pageSizeFound = true;
37
+ break;
38
+ }
39
+ }
40
+ if (!pageSizeFound) {
41
+ throw new Error("pageSize must be a valid option in pageLimits, pageLimits is set to [".concat(pageLimits, "]"));
42
+ }
43
+ }
44
+ const [internalPageSize, setInternalPageSize] = (0, _react.useState)(() => {
45
+ return (0, _utils.generatePagingOptions)(pageLimits)[0].value;
66
46
  });
67
47
  const {
68
48
  paginate,
69
- currentPage,
70
- pageData,
71
- totalItems
72
- } = (0, _hooks.usePagination)(data, {
73
- itemsPerPage: pageSize,
49
+ currentPage: internalCurrentPage,
50
+ pageData: internalData
51
+ } = (0, _hooks.usePagination)(externallyControlled ? [] : data, {
52
+ itemsPerPage: internalPageSize,
74
53
  autoResetPage: true
75
54
  });
76
- (0, _react.useEffect)(() => {
77
- const handleResize = () => {
78
- setSmallScreen(figureSmallScreen(descriptionRef));
79
- };
80
- window.addEventListener("resize", handleResize);
81
- return () => {
82
- window.removeEventListener("resize", handleResize);
83
- };
84
- }, [isSmallScreen]);
85
- const handlePageSizeChange = e => {
86
- paginate(1);
87
- setPageSize(parseInt(e.target.value));
88
- };
89
- const getDescription = () => {
90
- if (description) {
91
- return description;
92
- }
93
- const visibleCount = pageData.length;
94
- if (isSmallScreen) {
95
- return "".concat(visibleCount, " out of ").concat(totalItems);
55
+ const controlData = externallyControlled ? data : internalData;
56
+ const controlPageSize = externallyControlled ? props.pageSize : internalPageSize;
57
+ const controlTotalItems = externallyControlled ? props.totalItems : data.length;
58
+ const controlCurrentPage = externallyControlled ? props.currentPage : internalCurrentPage;
59
+ const handlePageChange = page => {
60
+ if (externallyControlled) {
61
+ props.onPageChange(page);
62
+ return;
96
63
  }
97
- if (visibleCount === totalItems && visibleCount > 1) {
98
- return "Showing all ".concat(totalItems, " ").concat(itemName, "s");
64
+ paginate(page);
65
+ };
66
+ const handlePageSizeChange = pageSize => {
67
+ if (externallyControlled) {
68
+ props.onPageSizeChange(pageSize);
69
+ return;
99
70
  }
100
- return "Showing ".concat(visibleCount, " out of ").concat(totalItems, " ").concat(itemName).concat(totalItems !== 1 ? "s" : "");
71
+ paginate(1);
72
+ setInternalPageSize(pageSize);
101
73
  };
102
- const totalPages = Math.ceil(data.length / pageSize);
103
- const clonedChildren = renderChildren(children, dataForwardProp, pageData);
104
- return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, position === "below" && clonedChildren, /*#__PURE__*/_react.default.createElement("div", _extends({
105
- className: (0, _classnames.default)("pagination", className)
106
- }, divProps, {
107
- role: "navigation"
108
- }), /*#__PURE__*/_react.default.createElement("div", {
109
- className: "description",
110
- ref: descriptionRef
111
- }, getDescription()), /*#__PURE__*/_react.default.createElement(_TablePaginationControls.default, {
112
- onPageChange: paginate,
113
- currentPage: currentPage,
114
- totalPages: totalPages
115
- }), /*#__PURE__*/_react.default.createElement(_Select.default, {
116
- className: "items-per-page",
117
- label: "Items per page",
118
- labelClassName: "u-off-screen",
119
- id: "itemsPerPage",
120
- options: generatePagingOptions(pageLimits),
121
- onChange: handlePageSizeChange,
122
- value: pageSize
123
- })), position === "above" && clonedChildren);
74
+ const clonedChildren = (0, _utils.renderChildren)(children, dataForwardProp, controlData);
75
+ const controls = /*#__PURE__*/_react.default.createElement(_TablePaginationControls.default, _extends({}, divProps, {
76
+ data: controlData,
77
+ className: className,
78
+ itemName: itemName,
79
+ description: description,
80
+ pageLimits: pageLimits,
81
+ totalItems: controlTotalItems,
82
+ currentPage: controlCurrentPage,
83
+ pageSize: controlPageSize,
84
+ onPageChange: handlePageChange,
85
+ onPageSizeChange: handlePageSizeChange
86
+ }));
87
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, position === "above" && controls, clonedChildren, position === "below" && controls);
124
88
  };
125
89
  var _default = exports.default = TablePagination;
@@ -1,16 +1,6 @@
1
- export type Props = {
2
- /**
3
- * Callback function to handle a change in page number
4
- */
5
- onPageChange: (pageNumber: number) => void;
6
- /**
7
- * The current page of the data
8
- */
9
- currentPage: number;
10
- /**
11
- * The total number of pages that exists within the data
12
- */
13
- totalPages: number;
14
- };
15
- declare const TablePaginationControls: ({ onPageChange, currentPage, totalPages, }: Props) => JSX.Element;
1
+ import { HTMLAttributes } from "react";
2
+ import { BasePaginationProps, ExternalControlProps, InternalControlProps } from "../TablePagination";
3
+ export type AllProps = BasePaginationProps & InternalControlProps & ExternalControlProps;
4
+ export type Props = Omit<AllProps, "externallyControlled" | "dataForwardProp" | "position"> & HTMLAttributes<HTMLDivElement>;
5
+ declare const TablePaginationControls: ({ data, className, itemName, description, pageLimits, totalItems, currentPage, pageSize, onPageChange, onPageSizeChange, ...divProps }: Props) => JSX.Element;
16
6
  export default TablePaginationControls;
@@ -4,18 +4,43 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
- var _propTypes = _interopRequireDefault(require("prop-types"));
8
7
  var _Button = _interopRequireDefault(require("../../Button"));
9
8
  var _Icon = _interopRequireDefault(require("../../Icon"));
10
9
  var _Input = _interopRequireDefault(require("../../Input"));
11
- var _react = _interopRequireDefault(require("react"));
10
+ var _Select = _interopRequireDefault(require("../../Select"));
11
+ var _react = _interopRequireWildcard(require("react"));
12
+ var _classnames = _interopRequireDefault(require("classnames"));
13
+ var _utils = require("../utils");
14
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
15
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
12
16
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17
+ function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
13
18
  const TablePaginationControls = _ref => {
14
19
  let {
15
- onPageChange,
20
+ data,
21
+ className,
22
+ itemName,
23
+ description,
24
+ pageLimits,
25
+ totalItems,
16
26
  currentPage,
17
- totalPages
27
+ pageSize,
28
+ onPageChange,
29
+ onPageSizeChange,
30
+ ...divProps
18
31
  } = _ref;
32
+ const descriptionRef = (0, _react.useRef)(null);
33
+ const isSmallScreen = (0, _utils.useFigureSmallScreen)({
34
+ descriptionRef
35
+ });
36
+ const totalPages = Math.ceil(totalItems / pageSize);
37
+ const descriptionDisplay = (0, _utils.getDescription)({
38
+ description,
39
+ data,
40
+ isSmallScreen,
41
+ totalItems,
42
+ itemName
43
+ });
19
44
  const handleDecrementPage = currentPage => {
20
45
  if (currentPage > 1) {
21
46
  onPageChange(currentPage - 1);
@@ -30,7 +55,17 @@ const TablePaginationControls = _ref => {
30
55
  const newPage = Math.min(totalPages, Math.max(1, parseInt(e.target.value)));
31
56
  onPageChange(newPage);
32
57
  };
33
- return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_Button.default, {
58
+ const handlePageSizeChange = e => {
59
+ onPageSizeChange(parseInt(e.target.value));
60
+ };
61
+ return /*#__PURE__*/_react.default.createElement("div", _extends({
62
+ className: (0, _classnames.default)("pagination", className)
63
+ }, divProps, {
64
+ role: "navigation"
65
+ }), /*#__PURE__*/_react.default.createElement("div", {
66
+ className: "description",
67
+ ref: descriptionRef
68
+ }, descriptionDisplay), /*#__PURE__*/_react.default.createElement(_Button.default, {
34
69
  "aria-label": "Previous page",
35
70
  className: "back",
36
71
  appearance: "base",
@@ -56,11 +91,14 @@ const TablePaginationControls = _ref => {
56
91
  onClick: () => handleIncrementPage(currentPage, totalPages)
57
92
  }, /*#__PURE__*/_react.default.createElement(_Icon.default, {
58
93
  name: "chevron-down"
59
- })));
60
- };
61
- TablePaginationControls.propTypes = {
62
- onPageChange: _propTypes.default.func.isRequired,
63
- currentPage: _propTypes.default.number.isRequired,
64
- totalPages: _propTypes.default.number.isRequired
94
+ })), /*#__PURE__*/_react.default.createElement(_Select.default, {
95
+ className: "items-per-page",
96
+ label: "Items per page",
97
+ labelClassName: "u-off-screen",
98
+ id: "itemsPerPage",
99
+ options: (0, _utils.generatePagingOptions)(pageLimits),
100
+ onChange: handlePageSizeChange,
101
+ value: pageSize
102
+ }));
65
103
  };
66
104
  var _default = exports.default = TablePaginationControls;
@@ -0,0 +1,29 @@
1
+ import { ReactElement, ReactNode, RefObject } from "react";
2
+ /**
3
+ * Determine if we are working with a small screen.
4
+ * 'small screen' in this case is relative to the width of the description div
5
+ */
6
+ export declare const figureSmallScreen: (descriptionRef: RefObject<HTMLDivElement>) => boolean;
7
+ /**
8
+ * Iterate direct react child components and override the value of the prop specified by @param dataForwardProp
9
+ * for those child components.
10
+ * @param children - react node children to iterate
11
+ * @param dataForwardProp - the name of the prop from the children components to override
12
+ * @param data - actual data to be passed to the prop specified by @param dataForwardProp
13
+ */
14
+ export declare const renderChildren: (children: ReactNode, dataForwardProp: string, data: unknown[]) => ReactElement<any, string | import("react").JSXElementConstructor<any>>[];
15
+ export declare const DEFAULT_PAGE_LIMITS: number[];
16
+ export declare const generatePagingOptions: (pageLimits: number[]) => {
17
+ value: number;
18
+ label: string;
19
+ }[];
20
+ export declare const getDescription: ({ description, data, isSmallScreen, totalItems, itemName, }: {
21
+ description: ReactNode;
22
+ data: unknown[];
23
+ isSmallScreen: boolean;
24
+ totalItems: number;
25
+ itemName: string;
26
+ }) => string | number | true | ReactElement<any, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode>;
27
+ export declare const useFigureSmallScreen: (args: {
28
+ descriptionRef: RefObject<HTMLDivElement>;
29
+ }) => boolean;
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useFigureSmallScreen = exports.renderChildren = exports.getDescription = exports.generatePagingOptions = exports.figureSmallScreen = exports.DEFAULT_PAGE_LIMITS = void 0;
7
+ var _react = require("react");
8
+ /**
9
+ * Determine if we are working with a small screen.
10
+ * 'small screen' in this case is relative to the width of the description div
11
+ */
12
+ const figureSmallScreen = descriptionRef => {
13
+ const descriptionElement = descriptionRef.current;
14
+ if (!descriptionElement) {
15
+ return true;
16
+ }
17
+ return descriptionElement.getBoundingClientRect().width < 230;
18
+ };
19
+
20
+ /**
21
+ * Iterate direct react child components and override the value of the prop specified by @param dataForwardProp
22
+ * for those child components.
23
+ * @param children - react node children to iterate
24
+ * @param dataForwardProp - the name of the prop from the children components to override
25
+ * @param data - actual data to be passed to the prop specified by @param dataForwardProp
26
+ */
27
+ exports.figureSmallScreen = figureSmallScreen;
28
+ const renderChildren = (children, dataForwardProp, data) => {
29
+ return _react.Children.map(children, child => {
30
+ return /*#__PURE__*/(0, _react.cloneElement)(child, {
31
+ [dataForwardProp]: data
32
+ });
33
+ });
34
+ };
35
+ exports.renderChildren = renderChildren;
36
+ const DEFAULT_PAGE_LIMITS = exports.DEFAULT_PAGE_LIMITS = [50, 100, 200];
37
+ const generatePagingOptions = pageLimits => {
38
+ return pageLimits.map(limit => ({
39
+ value: limit,
40
+ label: "".concat(limit, "/page")
41
+ }));
42
+ };
43
+ exports.generatePagingOptions = generatePagingOptions;
44
+ const getDescription = _ref => {
45
+ let {
46
+ description,
47
+ data,
48
+ isSmallScreen,
49
+ totalItems,
50
+ itemName
51
+ } = _ref;
52
+ if (description) {
53
+ return description;
54
+ }
55
+ const visibleCount = data.length;
56
+ if (isSmallScreen) {
57
+ return "".concat(visibleCount, " out of ").concat(totalItems);
58
+ }
59
+ if (visibleCount === totalItems && visibleCount > 1) {
60
+ return "Showing all ".concat(totalItems, " ").concat(itemName, "s");
61
+ }
62
+ return "Showing ".concat(visibleCount, " out of ").concat(totalItems, " ").concat(itemName).concat(totalItems !== 1 ? "s" : "");
63
+ };
64
+ exports.getDescription = getDescription;
65
+ const useFigureSmallScreen = args => {
66
+ const {
67
+ descriptionRef
68
+ } = args;
69
+ const [isSmallScreen, setSmallScreen] = (0, _react.useState)(false);
70
+ (0, _react.useEffect)(() => {
71
+ const handleResize = () => {
72
+ setSmallScreen(figureSmallScreen(descriptionRef));
73
+ };
74
+ window.addEventListener("resize", handleResize);
75
+ return () => {
76
+ window.removeEventListener("resize", handleResize);
77
+ };
78
+ }, [isSmallScreen, descriptionRef]);
79
+ return isSmallScreen;
80
+ };
81
+ exports.useFigureSmallScreen = useFigureSmallScreen;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonical/react-components",
3
- "version": "0.49.0",
3
+ "version": "0.50.0",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "author": "Huw Wilkins <huw.wilkins@canonical.com>",