@canonical/react-components 3.1.1 → 3.3.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 (45) hide show
  1. package/dist/components/ColumnSelector/ColumnSelector.d.ts +32 -0
  2. package/dist/components/ColumnSelector/ColumnSelector.js +86 -0
  3. package/dist/components/ColumnSelector/ColumnSelector.scss +15 -0
  4. package/dist/components/ColumnSelector/ColumnSelector.stories.d.ts +6 -0
  5. package/dist/components/ColumnSelector/ColumnSelector.stories.js +48 -0
  6. package/dist/components/ColumnSelector/ColumnSelector.test.d.ts +1 -0
  7. package/dist/components/ColumnSelector/ColumnSelectorStoriesHelper.d.ts +15 -0
  8. package/dist/components/ColumnSelector/ColumnSelectorStoriesHelper.js +150 -0
  9. package/dist/components/ColumnSelector/columnSelectorHelper.d.ts +3 -0
  10. package/dist/components/ColumnSelector/columnSelectorHelper.js +17 -0
  11. package/dist/components/ColumnSelector/index.d.ts +3 -0
  12. package/dist/components/ColumnSelector/index.js +26 -0
  13. package/dist/components/CustomSelect/CustomSelect.js +1 -1
  14. package/dist/components/CustomSelect/CustomSelect.stories.d.ts +6 -0
  15. package/dist/components/CustomSelect/CustomSelect.stories.js +103 -1
  16. package/dist/components/CustomSelect/CustomSelectDropdown/CustomSelectDropdown.d.ts +1 -0
  17. package/dist/components/MultiSelect/MultiSelect.d.ts +2 -1
  18. package/dist/components/MultiSelect/MultiSelect.js +14 -5
  19. package/dist/components/MultiSelect/MultiSelect.stories.d.ts +1 -0
  20. package/dist/components/MultiSelect/MultiSelect.stories.js +7 -1
  21. package/dist/esm/components/ColumnSelector/ColumnSelector.d.ts +32 -0
  22. package/dist/esm/components/ColumnSelector/ColumnSelector.js +79 -0
  23. package/dist/esm/components/ColumnSelector/ColumnSelector.scss +15 -0
  24. package/dist/esm/components/ColumnSelector/ColumnSelector.stories.d.ts +6 -0
  25. package/dist/esm/components/ColumnSelector/ColumnSelector.stories.js +39 -0
  26. package/dist/esm/components/ColumnSelector/ColumnSelector.test.d.ts +1 -0
  27. package/dist/esm/components/ColumnSelector/ColumnSelectorStoriesHelper.d.ts +15 -0
  28. package/dist/esm/components/ColumnSelector/ColumnSelectorStoriesHelper.js +142 -0
  29. package/dist/esm/components/ColumnSelector/columnSelectorHelper.d.ts +3 -0
  30. package/dist/esm/components/ColumnSelector/columnSelectorHelper.js +13 -0
  31. package/dist/esm/components/ColumnSelector/index.d.ts +3 -0
  32. package/dist/esm/components/ColumnSelector/index.js +2 -0
  33. package/dist/esm/components/CustomSelect/CustomSelect.js +1 -1
  34. package/dist/esm/components/CustomSelect/CustomSelect.stories.d.ts +6 -0
  35. package/dist/esm/components/CustomSelect/CustomSelect.stories.js +102 -0
  36. package/dist/esm/components/CustomSelect/CustomSelectDropdown/CustomSelectDropdown.d.ts +1 -0
  37. package/dist/esm/components/MultiSelect/MultiSelect.d.ts +2 -1
  38. package/dist/esm/components/MultiSelect/MultiSelect.js +14 -5
  39. package/dist/esm/components/MultiSelect/MultiSelect.stories.d.ts +1 -0
  40. package/dist/esm/components/MultiSelect/MultiSelect.stories.js +5 -0
  41. package/dist/esm/index.d.ts +2 -0
  42. package/dist/esm/index.js +1 -0
  43. package/dist/index.d.ts +2 -0
  44. package/dist/index.js +22 -0
  45. package/package.json +1 -1
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = exports.WithoutSorting = exports.WithDisabledItems = exports.SearchExample = exports.Disabled = exports.CondensedExample = void 0;
6
+ exports.default = exports.WithoutSorting = exports.WithDisabledItems = exports.SearchExample = exports.HelpText = exports.Disabled = exports.CondensedExample = void 0;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
8
  var _MultiSelect = require("./MultiSelect");
9
9
  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); }
@@ -114,4 +114,10 @@ const Disabled = exports.Disabled = {
114
114
  ...CondensedExample.args,
115
115
  disabled: true
116
116
  }
117
+ };
118
+ const HelpText = exports.HelpText = {
119
+ args: {
120
+ ...CondensedExample.args,
121
+ help: /*#__PURE__*/_react.default.createElement("span", null, "This is a help text, that should appear underneath the component.")
122
+ }
117
123
  };
@@ -0,0 +1,32 @@
1
+ import React, { HTMLProps } from "react";
2
+ import { ClassName, PropsWithSpread } from "../../types";
3
+ import "./ColumnSelector.scss";
4
+ export type Props = PropsWithSpread<{
5
+ /**
6
+ * Optional classes to add to the contextual menu.
7
+ */
8
+ className?: ClassName;
9
+ /**
10
+ * Columns of the table that can be hidden by the user
11
+ */
12
+ columns: string[];
13
+ /**
14
+ * Columns of the table hidden by the user
15
+ */
16
+ userHidden: string[];
17
+ /**
18
+ * Columns of the table hidden because the available space is not sufficient
19
+ */
20
+ sizeHidden: string[];
21
+ /**
22
+ * Function that sets columns hidden by the user
23
+ */
24
+ setUserHidden: (columns: string[]) => void;
25
+ }, HTMLProps<HTMLElement>>;
26
+ /**
27
+ This is a [React](https://reactjs.org/) component that extends from the Vanilla [Select](https://vanillaframework.io/docs/base/forms#select) element.
28
+ The aim of this component is to provide a dropdown menu to control the visibility of columns within a table.
29
+ This component allows users to customize their view, hiding or showing columns as needed, while also handling columns that are automatically hidden on smaller screens.
30
+ */
31
+ declare const ColumnSelector: ({ className, columns, userHidden, sizeHidden, setUserHidden, }: Props) => React.JSX.Element;
32
+ export default ColumnSelector;
@@ -0,0 +1,79 @@
1
+ import React from "react";
2
+ import classnames from "classnames";
3
+ import Tooltip from "../Tooltip";
4
+ import ContextualMenu from "../ContextualMenu";
5
+ import Icon from "../Icon";
6
+ import CheckboxInput from "../CheckboxInput";
7
+ import "./ColumnSelector.scss";
8
+ /**
9
+ This is a [React](https://reactjs.org/) component that extends from the Vanilla [Select](https://vanillaframework.io/docs/base/forms#select) element.
10
+ The aim of this component is to provide a dropdown menu to control the visibility of columns within a table.
11
+ This component allows users to customize their view, hiding or showing columns as needed, while also handling columns that are automatically hidden on smaller screens.
12
+ */
13
+ var ColumnSelector = _ref => {
14
+ var {
15
+ className,
16
+ columns,
17
+ userHidden,
18
+ sizeHidden,
19
+ setUserHidden
20
+ } = _ref;
21
+ var selectedCount = columns.length - userHidden.length;
22
+ var toggleHiddenColumn = column => {
23
+ if (userHidden.includes(column)) {
24
+ setUserHidden(userHidden.filter(c => c !== column));
25
+ } else {
26
+ setUserHidden([...userHidden, column]);
27
+ }
28
+ };
29
+ var wrapTooltip = (element, column) => {
30
+ if (!sizeHidden.includes(column)) return element;
31
+ return /*#__PURE__*/React.createElement(Tooltip, {
32
+ message: /*#__PURE__*/React.createElement(React.Fragment, null, "Screen is too narrow to fit the column.", /*#__PURE__*/React.createElement("br", null), "Disable columns above or use a bigger screen."),
33
+ position: "left"
34
+ }, element);
35
+ };
36
+ return /*#__PURE__*/React.createElement(ContextualMenu, {
37
+ className: classnames(className, "column-selector-toggle"),
38
+ dropdownProps: {
39
+ "aria-label": "columns menu"
40
+ },
41
+ position: "right",
42
+ toggleClassName: "has-icon",
43
+ toggleProps: {
44
+ "aria-label": "Columns selection toggle"
45
+ },
46
+ toggleLabel: /*#__PURE__*/React.createElement(Icon, {
47
+ name: "settings"
48
+ }),
49
+ toggleAppearance: "base",
50
+ title: "Columns"
51
+ }, /*#__PURE__*/React.createElement("div", {
52
+ className: "column-selector-column-list"
53
+ }, /*#__PURE__*/React.createElement(CheckboxInput, {
54
+ checked: userHidden.length === 0,
55
+ indeterminate: selectedCount > 0 && selectedCount < columns.length,
56
+ label: "".concat(selectedCount, " out of ").concat(columns.length, " columns selected"),
57
+ onChange: () => {
58
+ if (userHidden.length > 0) {
59
+ setUserHidden([]);
60
+ } else {
61
+ setUserHidden(columns);
62
+ }
63
+ }
64
+ }), /*#__PURE__*/React.createElement("hr", null), columns.map(column => /*#__PURE__*/React.createElement("div", {
65
+ key: column
66
+ }, wrapTooltip( /*#__PURE__*/React.createElement(CheckboxInput, {
67
+ "aria-label": column,
68
+ labelClassName: classnames({
69
+ "size-hidden": sizeHidden.includes(column)
70
+ }),
71
+ checked: !userHidden.includes(column),
72
+ label: column,
73
+ onChange: () => {
74
+ toggleHiddenColumn(column);
75
+ },
76
+ disabled: sizeHidden.includes(column)
77
+ }), column)))));
78
+ };
79
+ export default ColumnSelector;
@@ -0,0 +1,15 @@
1
+ @import "vanilla-framework";
2
+ @include vf-b-placeholders;
3
+ @include vf-p-icon-settings;
4
+
5
+ .column-selector-column-list {
6
+ min-width: 12rem;
7
+
8
+ .p-checkbox {
9
+ margin-left: 1rem;
10
+ }
11
+
12
+ .size-hidden > * {
13
+ opacity: 0.33;
14
+ }
15
+ }
@@ -0,0 +1,6 @@
1
+ import { Meta, StoryObj } from "@storybook/react";
2
+ import ColumnSelector from "./ColumnSelector";
3
+ declare const meta: Meta<typeof ColumnSelector>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof ColumnSelector>;
6
+ export declare const Default: Story;
@@ -0,0 +1,39 @@
1
+ import React, { useState } from "react";
2
+ import ColumnSelector from "./ColumnSelector";
3
+ import MainTable from "../MainTable";
4
+ import { getRows, FILESYSTEM, SNAPSHOTS, USER_HIDEABLE_COLUMNS, DESCRIPTION, CLUSTER_MEMBER, COLUMN_HEADERS } from "./ColumnSelectorStoriesHelper";
5
+ import { visibleHeaderColumns, visibleRowColumns } from "./columnSelectorHelper";
6
+ var meta = {
7
+ component: ColumnSelector,
8
+ tags: ["autodocs"]
9
+ };
10
+ export default meta;
11
+ export var Default = {
12
+ render: () => {
13
+ // eslint-disable-next-line react-hooks/rules-of-hooks
14
+ var [userHidden, setUserHidden] = useState([FILESYSTEM, SNAPSHOTS, CLUSTER_MEMBER]);
15
+ var sizeHidden = [DESCRIPTION];
16
+ var hiddenColumns = userHidden.concat(sizeHidden);
17
+ var ROWS = getRows();
18
+ var setUserHiddenAndSaveInLocalStorage = columns => {
19
+ setUserHidden(columns);
20
+ };
21
+ var displayedRows = visibleRowColumns(ROWS, hiddenColumns);
22
+ var displayedHeaders = visibleHeaderColumns(COLUMN_HEADERS.map(t => {
23
+ return {
24
+ content: t,
25
+ sortKey: t
26
+ };
27
+ }), hiddenColumns);
28
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ColumnSelector, {
29
+ columns: USER_HIDEABLE_COLUMNS,
30
+ userHidden: userHidden,
31
+ sizeHidden: sizeHidden,
32
+ setUserHidden: setUserHiddenAndSaveInLocalStorage
33
+ }), /*#__PURE__*/React.createElement(MainTable, {
34
+ rows: displayedRows,
35
+ headers: displayedHeaders
36
+ }));
37
+ },
38
+ name: "Default"
39
+ };
@@ -0,0 +1 @@
1
+ import "@testing-library/jest-dom";
@@ -0,0 +1,15 @@
1
+ import { MainTableRow } from "../MainTable/MainTable";
2
+ export declare const STATUS = "Status";
3
+ export declare const NAME = "Name";
4
+ export declare const TYPE = "Type";
5
+ export declare const CLUSTER_MEMBER = "Cluster member";
6
+ export declare const DESCRIPTION = "Description";
7
+ export declare const MEMORY = "Memory";
8
+ export declare const FILESYSTEM = "Root filesystem";
9
+ export declare const IPV4 = "IPv4";
10
+ export declare const IPV6 = "IPv6";
11
+ export declare const SNAPSHOTS = "Snapshots";
12
+ export declare const PROJECT = "Project";
13
+ export declare const COLUMN_HEADERS: string[];
14
+ export declare const USER_HIDEABLE_COLUMNS: string[];
15
+ export declare const getRows: () => MainTableRow[];
@@ -0,0 +1,142 @@
1
+ import React from "react";
2
+ export var STATUS = "Status";
3
+ export var NAME = "Name";
4
+ export var TYPE = "Type";
5
+ export var CLUSTER_MEMBER = "Cluster member";
6
+ export var DESCRIPTION = "Description";
7
+ export var MEMORY = "Memory";
8
+ export var FILESYSTEM = "Root filesystem";
9
+ export var IPV4 = "IPv4";
10
+ export var IPV6 = "IPv6";
11
+ export var SNAPSHOTS = "Snapshots";
12
+ export var PROJECT = "Project";
13
+ export var COLUMN_HEADERS = [NAME, TYPE, PROJECT, CLUSTER_MEMBER, MEMORY, FILESYSTEM, DESCRIPTION, IPV4, IPV6, SNAPSHOTS, STATUS];
14
+
15
+ // All columns except name and status
16
+ export var USER_HIDEABLE_COLUMNS = [TYPE, PROJECT, CLUSTER_MEMBER, MEMORY, FILESYSTEM, DESCRIPTION, IPV4, IPV6, SNAPSHOTS];
17
+ var INSTANCES = [{
18
+ name: "darling-bunny",
19
+ type: "VM",
20
+ project: "default",
21
+ clusterMember: "-",
22
+ memory: "1.4 GiB of 1.9 GiB",
23
+ fileSystem: "3.3 GiB of 9.5 GiB",
24
+ description: "Meet darling-bunny, a speedy little instance hopping along with your latest project. It's got the ears for listening to your commands and the nose for sniffing out the best performance. Handle with care, and remember to feed it plenty of CPU.",
25
+ ipv4: ["10.138.32.6"],
26
+ ipv6: ["fd42:6a38:859a:587d:216:3eff:fe43:167e", "fe80::216:3eff:fe43:167e", "fe80::1050:62ff:feb2:964c"],
27
+ status: "Running"
28
+ }, {
29
+ name: "sassy-salamander",
30
+ type: "VM",
31
+ project: "default",
32
+ clusterMember: "-",
33
+ memory: "1.4 GiB of 1.9 GiB",
34
+ fileSystem: "3.3 GiB of 9.5 GiB",
35
+ description: "sassy-salamander is a resilient and feisty instance, perfectly at home in any environment—from the scorching heat of a web server to the damp coolness of a database. Don't let its size fool you; it's quick, adaptable, and not afraid to tackle complex tasks.",
36
+ ipv4: ["10.138.32.6"],
37
+ ipv6: ["fd42:6a38:859a:587d:216:3eff:fe43:167e", "fe80::216:3eff:fe43:167e", "fe80::1050:62ff:feb2:964c"],
38
+ status: "Running"
39
+ }, {
40
+ name: "fun-feline",
41
+ type: "Container",
42
+ project: "default",
43
+ clusterMember: "-",
44
+ memory: "0.7 GiB of 1.9 GiB",
45
+ fileSystem: "1.1 GiB of 9.5 GiB",
46
+ description: "fun-feline is a purr-fectly agile and independent instance. It’s got nine lives for all your testing needs and a mischievous spirit for tackling the toughest tasks. Just be sure to give it enough RAM—it loves to stretch out.",
47
+ ipv4: ["10.138.32.6"],
48
+ ipv6: ["fd42:6a38:859a:587d:216:3eff:fe43:167e", "fe80::216:3eff:fe43:167e", "fe80::1050:62ff:feb2:964c"],
49
+ snapshots: ["1", "2"],
50
+ status: "Stopped"
51
+ }];
52
+ export var getRows = () => {
53
+ var instanceRows = INSTANCES.map(instance => {
54
+ var _instance$snapshots$l, _instance$snapshots;
55
+ return {
56
+ key: instance.name,
57
+ name: instance.name,
58
+ columns: [{
59
+ content: instance.name,
60
+ className: "u-truncate",
61
+ title: "Instance ".concat(instance.name),
62
+ role: "rowheader",
63
+ "aria-label": NAME,
64
+ // needed
65
+ onClick: () => console.log("Click on instance", instance.name)
66
+ }, {
67
+ content: /*#__PURE__*/React.createElement("span", null, instance.type),
68
+ className: "clickable-cell",
69
+ role: "cell",
70
+ "aria-label": TYPE,
71
+ // needed
72
+ onClick: () => console.log("Click on instance", instance.type)
73
+ }, {
74
+ content: /*#__PURE__*/React.createElement("a", null, instance.project),
75
+ role: "cell",
76
+ "aria-label": PROJECT // needed
77
+ }, {
78
+ content: /*#__PURE__*/React.createElement("a", null, instance.clusterMember),
79
+ role: "cell",
80
+ "aria-label": CLUSTER_MEMBER // needed
81
+ }, {
82
+ content: instance.memory,
83
+ className: "clickable-cell",
84
+ role: "cell",
85
+ "aria-label": MEMORY,
86
+ // needed
87
+ onClick: () => console.log("Click on instance", instance.memory)
88
+ }, {
89
+ content: instance.fileSystem,
90
+ className: "clickable-cell",
91
+ role: "cell",
92
+ "aria-label": FILESYSTEM,
93
+ // needed
94
+ onClick: () => console.log("Click on instance", instance.fileSystem)
95
+ }, {
96
+ content: /*#__PURE__*/React.createElement("div", {
97
+ className: "u-truncate",
98
+ title: instance.description
99
+ }, instance.description),
100
+ className: "clickable-cell",
101
+ role: "cell",
102
+ "aria-label": DESCRIPTION,
103
+ // needed
104
+ onClick: () => console.log("Click on instance", instance.description)
105
+ }, {
106
+ key: "ipv4-".concat(instance.ipv4.length),
107
+ content: instance.ipv4.join(", "),
108
+ className: "u-align--right clickable-cell",
109
+ role: "cell",
110
+ "aria-label": IPV4,
111
+ // needed
112
+ onClick: () => console.log("Click on instance", instance.ipv4)
113
+ }, {
114
+ key: "ipv6-".concat(instance.ipv6.length),
115
+ content: instance.ipv6.join(", "),
116
+ className: "clickable-cell",
117
+ role: "cell",
118
+ "aria-label": IPV6,
119
+ // needed
120
+ onClick: () => console.log("Click on instance", instance.ipv6)
121
+ }, {
122
+ content: (_instance$snapshots$l = (_instance$snapshots = instance.snapshots) === null || _instance$snapshots === void 0 ? void 0 : _instance$snapshots.length) !== null && _instance$snapshots$l !== void 0 ? _instance$snapshots$l : "0",
123
+ className: "u-align--right clickable-cell",
124
+ role: "cell",
125
+ "aria-label": SNAPSHOTS,
126
+ // needed
127
+ onClick: () => {
128
+ var _instance$snapshots2;
129
+ return console.log("Click on instance", (_instance$snapshots2 = instance.snapshots) === null || _instance$snapshots2 === void 0 ? void 0 : _instance$snapshots2.length);
130
+ }
131
+ }, {
132
+ content: instance.status,
133
+ role: "cell",
134
+ className: "clickable-cell",
135
+ "aria-label": STATUS,
136
+ // needed
137
+ onClick: () => console.log("Click on instance", instance.status)
138
+ }]
139
+ };
140
+ });
141
+ return instanceRows;
142
+ };
@@ -0,0 +1,3 @@
1
+ import { MainTableHeader, MainTableRow } from "../MainTable/MainTable";
2
+ export declare const visibleRowColumns: (rows: MainTableRow[], hiddenCols: string[]) => MainTableRow[];
3
+ export declare const visibleHeaderColumns: (headers: MainTableHeader[], hiddenCols: string[]) => MainTableHeader[];
@@ -0,0 +1,13 @@
1
+ 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; }
2
+ 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; }
3
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
4
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
5
+ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
6
+ export var visibleRowColumns = (rows, hiddenCols) => {
7
+ return rows.map(row => {
8
+ return _objectSpread(_objectSpread({}, row), {}, {
9
+ columns: row.columns.filter(item => !hiddenCols.includes(item["aria-label"]))
10
+ });
11
+ });
12
+ };
13
+ export var visibleHeaderColumns = (headers, hiddenCols) => headers.filter(item => typeof item.content !== "string" || !hiddenCols.includes(item.content));
@@ -0,0 +1,3 @@
1
+ export { default } from "./ColumnSelector";
2
+ export { visibleHeaderColumns, visibleRowColumns, } from "./columnSelectorHelper";
3
+ export type { Props as ColumnSelectorProps } from "./ColumnSelector";
@@ -0,0 +1,2 @@
1
+ export { default } from "./ColumnSelector";
2
+ export { visibleHeaderColumns, visibleRowColumns } from "./columnSelectorHelper";
@@ -75,7 +75,7 @@ var CustomSelect = _ref => {
75
75
  var selectedOption = options.find(option => option.value === value);
76
76
  var toggleLabel = /*#__PURE__*/React.createElement("span", {
77
77
  className: "toggle-label u-truncate"
78
- }, selectedOption ? getOptionText(selectedOption) : "Select an option");
78
+ }, selectedOption ? selectedOption.selectedLabel || getOptionText(selectedOption) : "Select an option");
79
79
  var handleSelect = value => {
80
80
  var _document$getElementB3;
81
81
  (_document$getElementB3 = document.getElementById(selectId)) === null || _document$getElementB3 === void 0 || _document$getElementB3.focus();
@@ -14,6 +14,12 @@ export declare const StandardOptions: Story;
14
14
  * In this case, the `text` property for each option is required and is used for display in the toggle, search and sort functionalities.
15
15
  */
16
16
  export declare const CustomOptions: Story;
17
+ /**
18
+ * If `label` is of `ReactNode` type. You can render custom content.
19
+ * In this case, the `selectedLabel` for each option is provided and will be displayed in the toggle instead of `text`
20
+ * The `text` property for each option is still required and is used for search and sort functionalities.
21
+ */
22
+ export declare const CustomOptionsAndSelectedLabel: Story;
17
23
  /**
18
24
  * For each option, if `disabled` is set to `true`, the option will be disabled.
19
25
  */
@@ -41,6 +41,95 @@ var generateCustomOptions = () => {
41
41
  disabled: false
42
42
  }];
43
43
  };
44
+ var generateCustomOptionsWithSelectedLabel = () => {
45
+ var options = [{
46
+ type: "ovn",
47
+ name: "ovntest",
48
+ config: {
49
+ "security.acls": "foo,bar"
50
+ }
51
+ }, {
52
+ type: "bridge",
53
+ name: "lxdbr0",
54
+ config: {}
55
+ }, {
56
+ type: "bridge",
57
+ name: "microbr0",
58
+ config: {}
59
+ }, {
60
+ type: "macvlan",
61
+ name: "macvlantest",
62
+ config: {}
63
+ }].map(network => {
64
+ var _network$config$secur;
65
+ return {
66
+ label: /*#__PURE__*/React.createElement("div", {
67
+ className: "label",
68
+ style: {
69
+ display: "flex",
70
+ gap: "5px"
71
+ }
72
+ }, /*#__PURE__*/React.createElement("span", {
73
+ title: network.name,
74
+ className: "network-option u-truncate",
75
+ style: {
76
+ width: "12rem"
77
+ }
78
+ }, network.name), /*#__PURE__*/React.createElement("span", {
79
+ title: network.type,
80
+ className: "network-option u-truncate",
81
+ style: {
82
+ width: "8rem"
83
+ }
84
+ }, network.type), /*#__PURE__*/React.createElement("span", {
85
+ title: "network ACLs",
86
+ className: "network-option u-truncate u-align--right",
87
+ style: {
88
+ paddingRight: "8px",
89
+ width: "4rem"
90
+ }
91
+ }, ((_network$config$secur = network.config["security.acls"]) === null || _network$config$secur === void 0 ? void 0 : _network$config$secur.length) || "-")),
92
+ value: network.name,
93
+ text: "".concat(network.name, " - ").concat(network.type),
94
+ disabled: false,
95
+ selectedLabel: /*#__PURE__*/React.createElement("span", null, network.name, "\xA0", /*#__PURE__*/React.createElement("span", {
96
+ className: "u-text--muted"
97
+ }, "(", network.type, ")"))
98
+ };
99
+ });
100
+ return options;
101
+ };
102
+ var getHeader = () => {
103
+ return /*#__PURE__*/React.createElement("div", {
104
+ className: "header",
105
+ style: {
106
+ backgroundColor: "$colors--theme--background-alt",
107
+ display: "flex",
108
+ gap: "$sph--small",
109
+ padding: "$sph--x-small $sph--small",
110
+ position: "sticky",
111
+ top: 0
112
+ }
113
+ }, /*#__PURE__*/React.createElement("span", {
114
+ className: "network-option u-no-margin--bottom",
115
+ style: {
116
+ color: "$colors--theme--text-default",
117
+ width: "12rem"
118
+ }
119
+ }, "Name"), /*#__PURE__*/React.createElement("span", {
120
+ className: "network-option u-no-margin--bottom",
121
+ style: {
122
+ color: "$colors--theme--text-default",
123
+ width: "8rem"
124
+ }
125
+ }, "Type"), /*#__PURE__*/React.createElement("span", {
126
+ className: "network-option u-no-margin--bottom",
127
+ style: {
128
+ color: "$colors--theme--text-default",
129
+ width: "4rem"
130
+ }
131
+ }, "ACLs"));
132
+ };
44
133
  var Template = _ref => {
45
134
  var props = _extends({}, (_objectDestructuringEmpty(_ref), _ref));
46
135
  var [selected, setSelected] = useState(props.value || "");
@@ -94,6 +183,19 @@ export var CustomOptions = {
94
183
  }
95
184
  };
96
185
 
186
+ /**
187
+ * If `label` is of `ReactNode` type. You can render custom content.
188
+ * In this case, the `selectedLabel` for each option is provided and will be displayed in the toggle instead of `text`
189
+ * The `text` property for each option is still required and is used for search and sort functionalities.
190
+ */
191
+ export var CustomOptionsAndSelectedLabel = {
192
+ args: {
193
+ options: generateCustomOptionsWithSelectedLabel(),
194
+ header: getHeader(),
195
+ dropdownClassName: "network-select-dropdown"
196
+ }
197
+ };
198
+
97
199
  /**
98
200
  * For each option, if `disabled` is set to `true`, the option will be disabled.
99
201
  */
@@ -4,6 +4,7 @@ export type CustomSelectOption = LiHTMLAttributes<HTMLLIElement> & {
4
4
  label: ReactNode;
5
5
  text?: string;
6
6
  disabled?: boolean;
7
+ selectedLabel?: ReactNode;
7
8
  };
8
9
  export type Props = {
9
10
  searchable?: "auto" | "always" | "never";
@@ -10,7 +10,8 @@ export type MultiSelectProps = {
10
10
  disabled?: boolean;
11
11
  error?: string;
12
12
  selectedItems?: MultiSelectItem[];
13
- help?: string;
13
+ help?: ReactNode;
14
+ helpClassName?: string;
14
15
  label?: string | null;
15
16
  listSelected?: boolean;
16
17
  onDeselectItem?: (item: MultiSelectItem) => void;
@@ -3,6 +3,7 @@ var _excluded = ["items", "selectedItems", "disabledItems", "header", "updateIte
3
3
  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); }
4
4
  function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
5
5
  function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
6
+ import classNames from "classnames";
6
7
  import React, { useEffect, useId, useMemo, useRef, useState } from "react";
7
8
  import "./MultiSelect.scss";
8
9
  import { Button, CheckboxInput, ContextualMenu, SearchBox } from "../../index";
@@ -172,13 +173,16 @@ export var MultiSelect = _ref4 => {
172
173
  scrollOverflow = false,
173
174
  isSortedAlphabetically = true,
174
175
  hasSelectedItemsFirst = true,
175
- id
176
+ id,
177
+ help,
178
+ helpClassName
176
179
  } = _ref4;
177
180
  var buttonRef = useRef(null);
178
181
  var [isDropdownOpen, setIsDropdownOpen] = useState(false);
179
182
  var [filter, setFilter] = useState("");
180
183
  var [internalSelectedItems, setInternalSelectedItems] = useState([]);
181
184
  var selectedItems = externalSelectedItems || internalSelectedItems;
185
+ var helpId = useId();
182
186
  var updateItems = newItems => {
183
187
  var uniqueItems = Array.from(new Set(newItems));
184
188
  setInternalSelectedItems(uniqueItems);
@@ -205,7 +209,7 @@ export var MultiSelect = _ref4 => {
205
209
  type: "button"
206
210
  }, "Clear"));
207
211
  }
208
- return /*#__PURE__*/React.createElement(ContextualMenu, {
212
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ContextualMenu, {
209
213
  className: "multi-select",
210
214
  onToggleMenu: isOpen => {
211
215
  if (!isOpen) {
@@ -269,7 +273,8 @@ export var MultiSelect = _ref4 => {
269
273
  className: "multi-select__condensed-text"
270
274
  }, listSelected && selectedItems.length > 0 ? selectedItemsLabel : placeholder !== null && placeholder !== void 0 ? placeholder : "Select items")),
271
275
  visible: isDropdownOpen,
272
- scrollOverflow: scrollOverflow
276
+ scrollOverflow: scrollOverflow,
277
+ "aria-describedby": help ? helpId : undefined
273
278
  }, /*#__PURE__*/React.createElement(MultiSelectDropdown, {
274
279
  id: dropdownId,
275
280
  isOpen: isDropdownOpen,
@@ -283,7 +288,10 @@ export var MultiSelect = _ref4 => {
283
288
  footer: footer,
284
289
  sortFn: isSortedAlphabetically ? sortAlphabetically : () => 0,
285
290
  hasSelectedItemsFirst: hasSelectedItemsFirst
286
- }));
291
+ })), help && /*#__PURE__*/React.createElement("p", {
292
+ className: classNames("p-form-help-text", helpClassName),
293
+ id: helpId
294
+ }, help));
287
295
  };
288
296
  MultiSelect.propTypes = {
289
297
  disabled: _pt.bool,
@@ -293,7 +301,8 @@ MultiSelect.propTypes = {
293
301
  value: _pt.oneOfType([_pt.string, _pt.number]).isRequired,
294
302
  group: _pt.string
295
303
  })),
296
- help: _pt.string,
304
+ help: _pt.node,
305
+ helpClassName: _pt.string,
297
306
  label: _pt.oneOfType([_pt.string, _pt.oneOf([null])]),
298
307
  listSelected: _pt.bool,
299
308
  onDeselectItem: _pt.func,
@@ -8,3 +8,4 @@ export declare const SearchExample: Story;
8
8
  export declare const WithDisabledItems: Story;
9
9
  export declare const WithoutSorting: Story;
10
10
  export declare const Disabled: Story;
11
+ export declare const HelpText: Story;
@@ -108,4 +108,9 @@ export var Disabled = {
108
108
  args: _objectSpread(_objectSpread({}, CondensedExample.args), {}, {
109
109
  disabled: true
110
110
  })
111
+ };
112
+ export var HelpText = {
113
+ args: _objectSpread(_objectSpread({}, CondensedExample.args), {}, {
114
+ help: /*#__PURE__*/React.createElement("span", null, "This is a help text, that should appear underneath the component.")
115
+ })
111
116
  };
@@ -16,6 +16,7 @@ export { default as Chip } from "./components/Chip";
16
16
  export { default as Code } from "./components/Code";
17
17
  export { default as CodeSnippet, CodeSnippetBlockAppearance, } from "./components/CodeSnippet";
18
18
  export { default as Col } from "./components/Col";
19
+ export { default as ColumnSelector, visibleHeaderColumns, visibleRowColumns, } from "./components/ColumnSelector";
19
20
  export { default as ConfirmationButton } from "./components/ConfirmationButton";
20
21
  export { default as ConfirmationModal } from "./components/ConfirmationModal";
21
22
  export { default as ContextualMenu } from "./components/ContextualMenu";
@@ -93,6 +94,7 @@ export type { ChipProps } from "./components/Chip";
93
94
  export type { CodeProps } from "./components/Code";
94
95
  export type { CodeSnippetProps, CodeSnippetBlockProps, CodeSnippetDropdownProps, } from "./components/CodeSnippet";
95
96
  export type { ColProps, ColSize } from "./components/Col";
97
+ export type { ColumnSelectorProps } from "./components/ColumnSelector";
96
98
  export type { ConfirmationButtonProps } from "./components/ConfirmationButton";
97
99
  export type { ConfirmationModalProps } from "./components/ConfirmationModal";
98
100
  export type { ContextualMenuProps, ContextualMenuDropdownProps, MenuLink, Position, } from "./components/ContextualMenu";
package/dist/esm/index.js CHANGED
@@ -16,6 +16,7 @@ export { default as Chip } from "./components/Chip";
16
16
  export { default as Code } from "./components/Code";
17
17
  export { default as CodeSnippet, CodeSnippetBlockAppearance } from "./components/CodeSnippet";
18
18
  export { default as Col } from "./components/Col";
19
+ export { default as ColumnSelector, visibleHeaderColumns, visibleRowColumns } from "./components/ColumnSelector";
19
20
  export { default as ConfirmationButton } from "./components/ConfirmationButton";
20
21
  export { default as ConfirmationModal } from "./components/ConfirmationModal";
21
22
  export { default as ContextualMenu } from "./components/ContextualMenu";