@canonical/react-components 0.55.0 → 0.57.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.
@@ -20,11 +20,11 @@ export type Props = {
20
20
  * An optional key to be used to track which section is selected.
21
21
  */
22
22
  sectionKey?: string;
23
- setExpanded?: (key: string | null, title: string | null) => void;
23
+ setExpanded?: (key: string | null, title: ReactNode | null) => void;
24
24
  /**
25
25
  * The title of the section.
26
26
  */
27
- title?: string;
27
+ title?: ReactNode;
28
28
  /**
29
29
  * Optional string describing heading element that should be used for the section titles.
30
30
  */
@@ -0,0 +1,123 @@
1
+ import React from "react";
2
+ import type { PropsWithSpread } from "../../types";
3
+ import type { ComponentType, ElementType, HTMLProps, PropsWithChildren, ReactNode } from "react";
4
+ import type { ExclusiveProps } from "../../types";
5
+ export type LogoDefaultElement = HTMLProps<HTMLAnchorElement>;
6
+ type PanelLogo<L = LogoDefaultElement> = ReactNode | PropsWithSpread<{
7
+ /**
8
+ * The url of the icon image.
9
+ */
10
+ icon: string;
11
+ /**
12
+ * The alt text for the icon image.
13
+ */
14
+ iconAlt?: string;
15
+ /**
16
+ * The url of the name image.
17
+ */
18
+ name: string;
19
+ /**
20
+ * The alt text for the name image.
21
+ */
22
+ nameAlt?: string;
23
+ /**
24
+ * The element or component to use for displaying the logo e.g. `a` or `NavLink`.
25
+ * @default a
26
+ */
27
+ component?: ElementType | ComponentType<L>;
28
+ }, L>;
29
+ type PanelToggle = {
30
+ /**
31
+ * The panel toggle label.
32
+ */
33
+ label: string;
34
+ /**
35
+ * The function to call when clicking the panel toggle.
36
+ */
37
+ onClick: () => void;
38
+ };
39
+ type LogoProps<L = LogoDefaultElement> = {
40
+ /**
41
+ * The panel logo content or attributes.
42
+ */
43
+ logo?: PanelLogo<L>;
44
+ };
45
+ type TitleProps = {
46
+ /**
47
+ * The panel title.
48
+ */
49
+ title: ReactNode;
50
+ /**
51
+ * Classes to apply to the title element.
52
+ */
53
+ titleClassName?: string;
54
+ /**
55
+ * The element to use for the panel title e.g. `h1`.
56
+ * @default h4
57
+ */
58
+ titleComponent?: ElementType;
59
+ /**
60
+ * An ID for the title element.
61
+ */
62
+ titleId?: string;
63
+ };
64
+ type HeaderProps<L = LogoDefaultElement> = ExclusiveProps<{
65
+ /**
66
+ * This prop can be used to replace the header area of the panel when the default implementation is not sufficient.
67
+ */
68
+ header: ReactNode;
69
+ }, {
70
+ /**
71
+ * Content that will be displayed in the controls area.
72
+ */
73
+ controls?: ReactNode;
74
+ /**
75
+ * Classes that will be applied to the controls element.
76
+ */
77
+ controlsClassName?: string;
78
+ /**
79
+ * Whether the header should be sticky.
80
+ */
81
+ stickyHeader?: boolean;
82
+ /**
83
+ * The panel toggle attributes.
84
+ */
85
+ toggle?: PanelToggle;
86
+ } & ExclusiveProps<LogoProps<L>, TitleProps>>;
87
+ export type Props<L = LogoDefaultElement> = {
88
+ /**
89
+ * The panel content.
90
+ */
91
+ children?: PropsWithChildren["children"];
92
+ /**
93
+ * Classes that are applied to the content container (when using `wrapContent`).
94
+ */
95
+ contentClassName?: string | null;
96
+ /**
97
+ * Classes that are applied to the top level panel element.
98
+ */
99
+ className?: string | null;
100
+ /**
101
+ * Whether to use the dark theme.
102
+ */
103
+ dark?: boolean;
104
+ /**
105
+ * Whether the panel should wrap the content in the `p-panel__content` element.
106
+ * @default true
107
+ */
108
+ wrapContent?: boolean;
109
+ /**
110
+ * A ref to pass to the top level panel element.
111
+ */
112
+ forwardRef?: React.Ref<HTMLDivElement> | null;
113
+ } & HeaderProps<L>;
114
+ /**
115
+ * This is a [React](https://reactjs.org/) component for panels in the
116
+ * [Vanilla](https://vanillaframework.io/docs/) layouts.
117
+ *
118
+ * The Panel component can be used in many areas of the application layout. It
119
+ * can be the child element of `AppAside`, `AppMain`, `AppNavigation`, `AppNavigationBar`
120
+ * and `AppStatus`.
121
+ */
122
+ declare const Panel: <L = LogoDefaultElement>({ forwardRef, children, className, contentClassName, controlsClassName, controls, dark, header, logo, stickyHeader, title, titleClassName, titleComponent: TitleComponent, titleId, toggle, wrapContent, ...props }: Props<L>) => React.JSX.Element;
123
+ export default Panel;
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _classnames = _interopRequireDefault(require("classnames"));
9
+ var _utils = require("../../utils");
10
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
+ 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); }
12
+ const generateLogo = logo => {
13
+ if ((0, _utils.isReactNode)(logo)) {
14
+ return logo;
15
+ }
16
+ const {
17
+ icon,
18
+ iconAlt,
19
+ name,
20
+ nameAlt,
21
+ component: Component = "a",
22
+ ...props
23
+ } = logo;
24
+ return /*#__PURE__*/_react.default.createElement(Component, _extends({
25
+ className: "p-panel__logo"
26
+ }, props), /*#__PURE__*/_react.default.createElement("img", {
27
+ className: "p-panel__logo-icon",
28
+ src: icon,
29
+ alt: iconAlt,
30
+ width: "24",
31
+ height: "24"
32
+ }), /*#__PURE__*/_react.default.createElement("img", {
33
+ className: "p-panel__logo-name is-fading-when-collapsed",
34
+ src: name,
35
+ alt: nameAlt,
36
+ height: "16"
37
+ }));
38
+ };
39
+
40
+ /**
41
+ * This is a [React](https://reactjs.org/) component for panels in the
42
+ * [Vanilla](https://vanillaframework.io/docs/) layouts.
43
+ *
44
+ * The Panel component can be used in many areas of the application layout. It
45
+ * can be the child element of `AppAside`, `AppMain`, `AppNavigation`, `AppNavigationBar`
46
+ * and `AppStatus`.
47
+ */
48
+ const Panel = _ref => {
49
+ let {
50
+ forwardRef,
51
+ children,
52
+ className,
53
+ contentClassName,
54
+ controlsClassName,
55
+ controls,
56
+ dark,
57
+ header,
58
+ logo,
59
+ stickyHeader,
60
+ title,
61
+ titleClassName,
62
+ titleComponent: TitleComponent = "h4",
63
+ titleId,
64
+ toggle,
65
+ wrapContent = true,
66
+ ...props
67
+ } = _ref;
68
+ return /*#__PURE__*/_react.default.createElement("div", _extends({}, props, {
69
+ className: (0, _classnames.default)("p-panel", className, {
70
+ "is-dark": dark
71
+ }),
72
+ ref: forwardRef
73
+ }), logo || title || controls || toggle ? /*#__PURE__*/_react.default.createElement("div", {
74
+ className: (0, _classnames.default)("p-panel__header", {
75
+ "is-sticky": stickyHeader
76
+ })
77
+ }, logo ? generateLogo(logo) : /*#__PURE__*/_react.default.createElement(TitleComponent, {
78
+ className: (0, _classnames.default)("p-panel__title", titleClassName),
79
+ id: titleId
80
+ }, title), /*#__PURE__*/_react.default.createElement("div", {
81
+ className: (0, _classnames.default)("p-panel__controls", controlsClassName)
82
+ }, toggle ? /*#__PURE__*/_react.default.createElement("span", {
83
+ role: "button",
84
+ tabIndex: 0,
85
+ className: "p-panel__toggle",
86
+ onClick: () => toggle.onClick(),
87
+ onKeyDown: () => toggle.onClick()
88
+ }, toggle.label) : null, controls)) : header, children && wrapContent ? /*#__PURE__*/_react.default.createElement("div", {
89
+ className: (0, _classnames.default)("p-panel__content", contentClassName)
90
+ }, children) : children);
91
+ };
92
+ var _default = exports.default = Panel;
@@ -0,0 +1,17 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import Panel from "./Panel";
3
+ declare const meta: Meta<typeof Panel>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof Panel>;
6
+ export declare const Default: Story;
7
+ export declare const Header: Story;
8
+ /**
9
+ * The logo may be provided as attributes to use the standard logo. If this is
10
+ * not sufficient the a `ReactNode` can be passed to the `logo` prop instead.
11
+ */
12
+ export declare const Logo: Story;
13
+ /**
14
+ * If the default header does not meet your needs then a `ReactNode` can be
15
+ * passed to the `header` prop to replace the header.
16
+ */
17
+ export declare const CustomHeader: Story;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = exports.Logo = exports.Header = exports.Default = exports.CustomHeader = void 0;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _Panel = _interopRequireDefault(require("./Panel"));
9
+ var _Button = _interopRequireDefault(require("../Button"));
10
+ var _Icon = _interopRequireDefault(require("../Icon"));
11
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12
+ const meta = {
13
+ component: _Panel.default,
14
+ tags: ["autodocs"]
15
+ };
16
+ var _default = exports.default = meta;
17
+ const Default = exports.Default = {
18
+ args: {
19
+ children: "Panel content",
20
+ title: "Panel"
21
+ }
22
+ };
23
+ const Header = exports.Header = {
24
+ args: {
25
+ controls: /*#__PURE__*/_react.default.createElement(_Button.default, {
26
+ appearance: "positive"
27
+ }, /*#__PURE__*/_react.default.createElement(_Icon.default, {
28
+ name: "plus",
29
+ light: true
30
+ }), " Create"),
31
+ title: "Panel title",
32
+ titleComponent: "h1"
33
+ }
34
+ };
35
+
36
+ /**
37
+ * The logo may be provided as attributes to use the standard logo. If this is
38
+ * not sufficient the a `ReactNode` can be passed to the `logo` prop instead.
39
+ */
40
+ const Logo = exports.Logo = {
41
+ args: {
42
+ logo: {
43
+ icon: "https://assets.ubuntu.com/v1/7144ec6d-logo-jaas-icon.svg",
44
+ name: "https://assets.ubuntu.com/v1/a85f7947-juju_black-text-only.svg",
45
+ nameAlt: "Juju"
46
+ }
47
+ }
48
+ };
49
+
50
+ /**
51
+ * If the default header does not meet your needs then a `ReactNode` can be
52
+ * passed to the `header` prop to replace the header.
53
+ */
54
+ const CustomHeader = exports.CustomHeader = {
55
+ args: {
56
+ header: /*#__PURE__*/_react.default.createElement("div", {
57
+ className: "p-panel__header"
58
+ }, "This header replaces the entire header area")
59
+ }
60
+ };
@@ -0,0 +1 @@
1
+ export { default, type Props as PanelProps, type LogoDefaultElement as PanelLogoDefaultElement, } from "./Panel";
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "default", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _Panel.default;
10
+ }
11
+ });
12
+ var _Panel = _interopRequireDefault(require("./Panel"));
13
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
package/dist/index.d.ts CHANGED
@@ -31,6 +31,7 @@ export { default as Notification, NotificationSeverity, } from "./components/Not
31
31
  export { NotificationConsumer, NotificationProvider, useNotify, info, success, failure, queue, } from "./components/NotificationProvider";
32
32
  export { default as LoginPageLayout } from "./components/LoginPageLayout";
33
33
  export { default as Pagination } from "./components/Pagination";
34
+ export { default as Panel } from "./components/Panel";
34
35
  export { default as PasswordToggle } from "./components/PasswordToggle";
35
36
  export { default as RadioInput } from "./components/RadioInput";
36
37
  export { default as Row } from "./components/Row";
@@ -82,6 +83,7 @@ export type { NotificationProps } from "./components/Notification";
82
83
  export type { NotificationAction, NotificationType, QueuedNotification, NotificationHelper, } from "./components/NotificationProvider";
83
84
  export type { LoginPageLayoutProps } from "./components/LoginPageLayout";
84
85
  export type { PaginationProps } from "./components/Pagination";
86
+ export type { PanelProps } from "./components/Panel";
85
87
  export type { RadioInputProps } from "./components/RadioInput";
86
88
  export type { RowProps } from "./components/Row";
87
89
  export type { SearchAndFilterProps } from "./components/SearchAndFilter";
package/dist/index.js CHANGED
@@ -46,6 +46,7 @@ var _exportNames = {
46
46
  queue: true,
47
47
  LoginPageLayout: true,
48
48
  Pagination: true,
49
+ Panel: true,
49
50
  PasswordToggle: true,
50
51
  RadioInput: true,
51
52
  Row: true,
@@ -302,6 +303,12 @@ Object.defineProperty(exports, "Pagination", {
302
303
  return _Pagination.default;
303
304
  }
304
305
  });
306
+ Object.defineProperty(exports, "Panel", {
307
+ enumerable: true,
308
+ get: function () {
309
+ return _Panel.default;
310
+ }
311
+ });
305
312
  Object.defineProperty(exports, "PasswordToggle", {
306
313
  enumerable: true,
307
314
  get: function () {
@@ -574,6 +581,7 @@ var _Notification = _interopRequireWildcard(require("./components/Notification")
574
581
  var _NotificationProvider = require("./components/NotificationProvider");
575
582
  var _LoginPageLayout = _interopRequireDefault(require("./components/LoginPageLayout"));
576
583
  var _Pagination = _interopRequireDefault(require("./components/Pagination"));
584
+ var _Panel = _interopRequireDefault(require("./components/Panel"));
577
585
  var _PasswordToggle = _interopRequireDefault(require("./components/PasswordToggle"));
578
586
  var _RadioInput = _interopRequireDefault(require("./components/RadioInput"));
579
587
  var _Row = _interopRequireDefault(require("./components/Row"));
@@ -35,3 +35,4 @@ export type TSFixMe = any;
35
35
  * defined in EnumLike.
36
36
  */
37
37
  export type ValueOf<T> = T[keyof T];
38
+ export type ExclusiveProps<A, B> = (A & Partial<Record<keyof B, never>>) | (B & Partial<Record<keyof A, never>>);
package/dist/utils.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { NavLink, NavLinkAnchor, NavLinkButton } from "./components/Navigation";
2
+ import { ReactNode } from "react";
2
3
  export declare const IS_DEV: boolean;
3
4
  /**
4
5
  * Find substring and wrap in <strong /> tag
@@ -20,3 +21,7 @@ export declare const isNavigationAnchor: (link: NavLink) => link is NavLinkAncho
20
21
  * @param link - The navigation item.
21
22
  */
22
23
  export declare const isNavigationButton: (link: NavLink) => link is NavLinkButton;
24
+ /**
25
+ * A typeguard for whether an element is a ReactNode.
26
+ */
27
+ export declare const isReactNode: (element: unknown) => element is ReactNode;
package/dist/utils.js CHANGED
@@ -3,7 +3,8 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.isNavigationButton = exports.isNavigationAnchor = exports.highlightSubString = exports.IS_DEV = void 0;
6
+ exports.isReactNode = exports.isNavigationButton = exports.isNavigationAnchor = exports.highlightSubString = exports.IS_DEV = void 0;
7
+ var _react = require("react");
7
8
  const IS_DEV = exports.IS_DEV = process.env.NODE_ENV === "development";
8
9
 
9
10
  /**
@@ -40,4 +41,10 @@ const isNavigationAnchor = link => !!link.url;
40
41
  */
41
42
  exports.isNavigationAnchor = isNavigationAnchor;
42
43
  const isNavigationButton = link => !link.url;
43
- exports.isNavigationButton = isNavigationButton;
44
+
45
+ /**
46
+ * A typeguard for whether an element is a ReactNode.
47
+ */
48
+ exports.isNavigationButton = isNavigationButton;
49
+ const isReactNode = element => /*#__PURE__*/(0, _react.isValidElement)(element);
50
+ exports.isReactNode = isReactNode;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonical/react-components",
3
- "version": "0.55.0",
3
+ "version": "0.57.0",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "author": "Huw Wilkins <huw.wilkins@canonical.com>",