@spaced-out/ui-design-system 0.1.48 → 0.1.50

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.
@@ -3,8 +3,8 @@ aditya-kaushal
3
3
  amet
4
4
  Anant
5
5
  argstable
6
- ashwini-sensehq
7
6
  Ashwini
7
+ ashwini-sensehq
8
8
  atleast
9
9
  autodocs
10
10
  circlehollow
@@ -43,6 +43,7 @@ preminor
43
43
  prepatch
44
44
  progressbar
45
45
  proxysense
46
+ rerouter
46
47
  rgba
47
48
  sbdocs
48
49
  Stapleton
package/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [0.1.50](https://github.com/spaced-out/ui-design-system/compare/v0.1.49...v0.1.50) (2023-09-15)
6
+
7
+
8
+ ### Features
9
+
10
+ * external file deletion support for file upload ([32cf857](https://github.com/spaced-out/ui-design-system/commit/32cf857a9688c2a20b1214381caccde699c192c4))
11
+
12
+ ### [0.1.49](https://github.com/spaced-out/ui-design-system/compare/v0.1.48...v0.1.49) (2023-09-15)
13
+
14
+
15
+ ### Features
16
+
17
+ * breadcrumbs component and link accessibility fixes ([#141](https://github.com/spaced-out/ui-design-system/issues/141)) ([609762e](https://github.com/spaced-out/ui-design-system/commit/609762e55cddb1a50fb56d17e4dcc31fdbcf1cd1))
18
+
5
19
  ### [0.1.48](https://github.com/spaced-out/ui-design-system/compare/v0.1.48-beta.1...v0.1.48) (2023-09-12)
6
20
 
7
21
 
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.BreadcrumbLink = void 0;
7
+ var React = _interopRequireWildcard(require("react"));
8
+ var _classify = _interopRequireDefault(require("../../../utils/classify"));
9
+ var _Link = require("../../Link");
10
+ var _BreadcrumbLinkModule = _interopRequireDefault(require("./BreadcrumbLink.module.css"));
11
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
13
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
14
+ 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); }
15
+ const BreadcrumbLink = /*#__PURE__*/React.forwardRef((_ref, ref) => {
16
+ let {
17
+ id,
18
+ className,
19
+ as,
20
+ underline = false,
21
+ isActive,
22
+ onClick,
23
+ href,
24
+ to,
25
+ ...props
26
+ } = _ref;
27
+ return /*#__PURE__*/React.createElement(_Link.Link, _extends({}, props, {
28
+ to: to ?? href,
29
+ "data-testid": "BreadcrumbLink",
30
+ ref: ref,
31
+ as: as || 'subTitleExtraSmall',
32
+ underline: underline,
33
+ disabled: isActive,
34
+ className: (0, _classify.default)(_BreadcrumbLinkModule.default.breadcrumbLink, {
35
+ [_BreadcrumbLinkModule.default.active]: isActive ?? window.location.href.includes(to ?? href)
36
+ }, className),
37
+ onClick: e => {
38
+ onClick?.(e, id);
39
+ }
40
+ }));
41
+ });
42
+ exports.BreadcrumbLink = BreadcrumbLink;
43
+ BreadcrumbLink.displayName = 'BreadcrumbLink';
@@ -0,0 +1,58 @@
1
+ // @flow strict
2
+
3
+ import * as React from 'react';
4
+
5
+ import classify from '../../../utils/classify';
6
+ import type {LinkProps} from '../../Link';
7
+ import {Link} from '../../Link';
8
+
9
+ import css from './BreadcrumbLink.module.css';
10
+
11
+
12
+ export type BreadcrumbLinkProps = {
13
+ ...LinkProps,
14
+ isActive?: boolean,
15
+ id: string,
16
+ onClick?: (SyntheticEvent<HTMLElement>, id: string) => mixed,
17
+ ...
18
+ };
19
+
20
+ export const BreadcrumbLink: React$AbstractComponent<
21
+ BreadcrumbLinkProps,
22
+ ?HTMLAnchorElement,
23
+ > = React.forwardRef<BreadcrumbLinkProps, ?HTMLAnchorElement>(
24
+ (
25
+ {
26
+ id,
27
+ className,
28
+ as,
29
+ underline = false,
30
+ isActive,
31
+ onClick,
32
+ href,
33
+ to,
34
+ ...props
35
+ }: BreadcrumbLinkProps,
36
+ ref,
37
+ ) => (
38
+ <Link
39
+ {...props}
40
+ to={to ?? href}
41
+ data-testid="BreadcrumbLink"
42
+ ref={ref}
43
+ as={as || 'subTitleExtraSmall'}
44
+ underline={underline}
45
+ disabled={isActive}
46
+ className={classify(
47
+ css.breadcrumbLink,
48
+ {[css.active]: isActive ?? window.location.href.includes(to ?? href)},
49
+ className,
50
+ )}
51
+ onClick={(e) => {
52
+ onClick?.(e, id);
53
+ }}
54
+ />
55
+ ),
56
+ );
57
+
58
+ BreadcrumbLink.displayName = 'BreadcrumbLink';
@@ -0,0 +1,28 @@
1
+ @value (
2
+ colorTextSecondary,
3
+ colorTextPrimary,
4
+ colorTextClickable
5
+ ) from '../../../styles/variables/_color.css';
6
+
7
+ .breadcrumbLink {
8
+ display: flex;
9
+ color: colorTextSecondary;
10
+ }
11
+
12
+ .breadcrumbLink:hover {
13
+ color: colorTextClickable;
14
+ }
15
+
16
+ .breadcrumbLink:focus,
17
+ .active {
18
+ color: colorTextPrimary;
19
+ }
20
+
21
+ .active {
22
+ pointer-events: none;
23
+ cursor: initial;
24
+ }
25
+
26
+ .active:hover {
27
+ color: colorTextPrimary;
28
+ }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ var _BreadcrumbLink = require("./BreadcrumbLink");
7
+ Object.keys(_BreadcrumbLink).forEach(function (key) {
8
+ if (key === "default" || key === "__esModule") return;
9
+ if (key in exports && exports[key] === _BreadcrumbLink[key]) return;
10
+ Object.defineProperty(exports, key, {
11
+ enumerable: true,
12
+ get: function () {
13
+ return _BreadcrumbLink[key];
14
+ }
15
+ });
16
+ });
@@ -0,0 +1,3 @@
1
+ // @flow strict
2
+
3
+ export * from './BreadcrumbLink';
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Breadcrumbs = void 0;
7
+ var React = _interopRequireWildcard(require("react"));
8
+ var _classify = _interopRequireDefault(require("../../utils/classify"));
9
+ var _Icon = require("../Icon");
10
+ var _BreadcrumbsModule = _interopRequireDefault(require("./Breadcrumbs.module.css"));
11
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
13
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
14
+
15
+ const BreadcrumbSeparator = () => /*#__PURE__*/React.createElement(_Icon.Icon, {
16
+ className: _BreadcrumbsModule.default.separator,
17
+ name: "chevron-right",
18
+ size: "small",
19
+ color: "secondary"
20
+ });
21
+ const Breadcrumbs = /*#__PURE__*/React.forwardRef((_ref, ref) => {
22
+ let {
23
+ classNames,
24
+ children,
25
+ linkComponent
26
+ } = _ref;
27
+ const total = React.Children.count(children);
28
+ return /*#__PURE__*/React.createElement("div", {
29
+ ref: ref,
30
+ "data-testid": "Breadcrumbs",
31
+ className: (0, _classify.default)(_BreadcrumbsModule.default.wrapper, classNames?.wrapper)
32
+ }, React.Children.map(children, (child, idx) =>
33
+ /*#__PURE__*/
34
+ // eslint-disable-next-line react/no-array-index-key
35
+ React.createElement(React.Fragment, {
36
+ key: `crumb-${idx}`
37
+ }, /*#__PURE__*/React.isValidElement(child) && child?.type?.displayName === 'BreadcrumbLink' ? /*#__PURE__*/React.cloneElement(child, {
38
+ linkComponent
39
+ }) : child, idx < total - 1 && /*#__PURE__*/React.createElement(BreadcrumbSeparator, {
40
+ "aria-hidden": true
41
+ }))));
42
+ });
43
+ exports.Breadcrumbs = Breadcrumbs;
@@ -0,0 +1,59 @@
1
+ // @flow strict
2
+
3
+ import * as React from 'react';
4
+
5
+ import classify from '../../utils/classify';
6
+ import {Icon} from '../Icon';
7
+ import type {BaseLinkProps} from '../Link';
8
+
9
+ import css from './Breadcrumbs.module.css';
10
+
11
+
12
+ type ClassNames = $ReadOnly<{wrapper?: string}>;
13
+
14
+ export type BreadcrumbsProps = {
15
+ classNames?: ClassNames,
16
+ children: React.Node,
17
+ linkComponent?: React.AbstractComponent<BaseLinkProps, ?HTMLAnchorElement>,
18
+ };
19
+
20
+ const BreadcrumbSeparator = (): React.Node => (
21
+ <Icon
22
+ className={css.separator}
23
+ name="chevron-right"
24
+ size="small"
25
+ color="secondary"
26
+ />
27
+ );
28
+
29
+ export const Breadcrumbs: React$AbstractComponent<
30
+ BreadcrumbsProps,
31
+ HTMLDivElement,
32
+ > = React.forwardRef<BreadcrumbsProps, HTMLDivElement>(
33
+ ({classNames, children, linkComponent}: BreadcrumbsProps, ref) => {
34
+ const total = React.Children.count(children);
35
+
36
+ return (
37
+ <div
38
+ ref={ref}
39
+ data-testid="Breadcrumbs"
40
+ className={classify(css.wrapper, classNames?.wrapper)}
41
+ >
42
+ {React.Children.map(children, (child, idx) => (
43
+ // eslint-disable-next-line react/no-array-index-key
44
+ <React.Fragment key={`crumb-${idx}`}>
45
+ {/*add linkComponent to BreadcrumbLinks only,
46
+ otherwise return original element */}
47
+ {React.isValidElement(child) &&
48
+ child?.type?.displayName === 'BreadcrumbLink'
49
+ ? React.cloneElement(child, {linkComponent})
50
+ : child}
51
+
52
+ {/*Add Separator*/}
53
+ {idx < total - 1 && <BreadcrumbSeparator aria-hidden />}
54
+ </React.Fragment>
55
+ ))}
56
+ </div>
57
+ );
58
+ },
59
+ );
@@ -0,0 +1,14 @@
1
+ @value (
2
+ spaceSmall
3
+ ) from '../../styles/variables/_space.css';
4
+
5
+ .wrapper {
6
+ display: flex;
7
+ align-items: center;
8
+ flex-wrap: wrap;
9
+ }
10
+
11
+ .separator {
12
+ margin-left: spaceSmall;
13
+ margin-right: spaceSmall;
14
+ }
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ var _BreadcrumbLink = require("./BreadcrumbLink");
7
+ Object.keys(_BreadcrumbLink).forEach(function (key) {
8
+ if (key === "default" || key === "__esModule") return;
9
+ if (key in exports && exports[key] === _BreadcrumbLink[key]) return;
10
+ Object.defineProperty(exports, key, {
11
+ enumerable: true,
12
+ get: function () {
13
+ return _BreadcrumbLink[key];
14
+ }
15
+ });
16
+ });
17
+ var _Breadcrumbs = require("./Breadcrumbs");
18
+ Object.keys(_Breadcrumbs).forEach(function (key) {
19
+ if (key === "default" || key === "__esModule") return;
20
+ if (key in exports && exports[key] === _Breadcrumbs[key]) return;
21
+ Object.defineProperty(exports, key, {
22
+ enumerable: true,
23
+ get: function () {
24
+ return _Breadcrumbs[key];
25
+ }
26
+ });
27
+ });
@@ -0,0 +1,4 @@
1
+ // @flow strict
2
+
3
+ export * from './BreadcrumbLink';
4
+ export * from './Breadcrumbs';
@@ -53,7 +53,8 @@ const Chip = /*#__PURE__*/React.forwardRef((_ref, ref) => {
53
53
  [_ChipModule.default.small]: size === 'small',
54
54
  [_ChipModule.default.dismissable]: dismissable,
55
55
  [_ChipModule.default.withIcon]: !!iconName && size !== 'small',
56
- [_ChipModule.default.disabled]: disabled
56
+ [_ChipModule.default.disabled]: disabled,
57
+ [_ChipModule.default.noHoverState]: showStatusIndicator
57
58
  }, classNames?.wrapper),
58
59
  onClick: onClick
59
60
  }), showStatusIndicator && size !== 'small' && /*#__PURE__*/React.createElement(_StatusIndicator.StatusIndicator, {
@@ -91,6 +91,7 @@ export const Chip: React$AbstractComponent<ChipProps, HTMLDivElement> =
91
91
  [css.dismissable]: dismissable,
92
92
  [css.withIcon]: !!iconName && size !== 'small',
93
93
  [css.disabled]: disabled,
94
+ [css.noHoverState]: showStatusIndicator,
94
95
  },
95
96
  classNames?.wrapper,
96
97
  )}
@@ -55,7 +55,11 @@
55
55
  background-color: colorNeutralLightest;
56
56
  }
57
57
 
58
- .primary:hover {
58
+ .noHoverState {
59
+ cursor: initial;
60
+ }
61
+
62
+ .primary:not(.noHoverState):hover {
59
63
  background-color: colorNeutralLight;
60
64
  }
61
65
 
@@ -63,7 +67,7 @@
63
67
  background-color: colorInformationLightest;
64
68
  }
65
69
 
66
- .information:hover {
70
+ .information:not(.noHoverState):hover {
67
71
  background-color: colorInformationLight;
68
72
  }
69
73
 
@@ -71,7 +75,7 @@
71
75
  background-color: colorSuccessLightest;
72
76
  }
73
77
 
74
- .success:hover {
78
+ .success:not(.noHoverState):hover {
75
79
  background-color: colorSuccessLight;
76
80
  }
77
81
 
@@ -79,7 +83,7 @@
79
83
  background-color: colorWarningLightest;
80
84
  }
81
85
 
82
- .warning:hover {
86
+ .warning:not(.noHoverState):hover {
83
87
  background-color: colorWarningLight;
84
88
  }
85
89
 
@@ -87,7 +91,7 @@
87
91
  background-color: colorDangerLightest;
88
92
  }
89
93
 
90
- .danger:hover {
94
+ .danger:not(.noHoverState):hover {
91
95
  background-color: colorDangerLight;
92
96
  }
93
97
 
@@ -96,7 +100,7 @@
96
100
  background-color: colorFillNone;
97
101
  }
98
102
 
99
- .secondary:hover {
103
+ .secondary:not(.noHoverState):hover {
100
104
  background-color: colorGrayLightest;
101
105
  }
102
106
 
@@ -18,7 +18,7 @@ type ClassNames = $ReadOnly<{
18
18
  export type FileBlockProps = {
19
19
  classNames?: ClassNames,
20
20
  fileObject: FileObject,
21
- onFileRefreshClick?: (file: FileObject) => void,
21
+ onFileRefreshClick?: (file: FileObject) => mixed,
22
22
  handleFileClear?: (id: string) => mixed,
23
23
  };
24
24
 
@@ -31,7 +31,8 @@ const FileUploadBase = (props, ref) => {
31
31
  onRejectedFilesDrop,
32
32
  onFileClear,
33
33
  onFileRefreshClick,
34
- maxFiles = 1
34
+ maxFiles = 1,
35
+ handleFileDeletionExternally
35
36
  } = props;
36
37
 
37
38
  // Get file upload state from useFileUpload hook
@@ -62,6 +63,7 @@ const FileUploadBase = (props, ref) => {
62
63
  moveFileToProgress,
63
64
  moveFileToSuccess,
64
65
  moveFileToReject,
66
+ handleFileClear,
65
67
  setShowReUpload,
66
68
  validFiles,
67
69
  rejectedFiles,
@@ -95,7 +97,7 @@ const FileUploadBase = (props, ref) => {
95
97
  }, /*#__PURE__*/React.createElement(_FileBlock.FileBlock, {
96
98
  fileObject: fileObject,
97
99
  onFileRefreshClick: onFileRefreshClick,
98
- handleFileClear: handleFileClear
100
+ handleFileClear: handleFileDeletionExternally ? onFileClear : handleFileClear
99
101
  })))));
100
102
  };
101
103
  const FileUpload = /*#__PURE__*/React.forwardRef(FileUploadBase);
@@ -23,8 +23,15 @@ type ClassNames = $ReadOnly<{
23
23
 
24
24
  export type FileProgress = number | 'indeterminate';
25
25
 
26
+ type LocalFileProps = {
27
+ name?: string,
28
+ type?: string,
29
+ size?: number,
30
+ ...
31
+ };
32
+
26
33
  export type FileObject = {
27
- file: File,
34
+ file: File | LocalFileProps,
28
35
  id: string,
29
36
  reject?: boolean,
30
37
  rejectReason?: string,
@@ -53,6 +60,7 @@ export type FileUploadRef = {
53
60
  moveFileToSuccess: (id: string, successMessage?: string) => mixed,
54
61
  moveFileToReject: (id: string, rejectReason?: string) => mixed,
55
62
  setShowReUpload: (id: string, showReUpload?: boolean) => mixed,
63
+ handleFileClear: (id: string) => mixed,
56
64
  validFiles: Array<FileObject>,
57
65
  rejectedFiles: Array<FileObject>,
58
66
  files: Array<FileObject>,
@@ -66,12 +74,12 @@ export type FileUploadBaseProps = {
66
74
  disabled?: boolean,
67
75
 
68
76
  // File drop callbacks
69
- onValidFilesDrop?: (acceptedFiles: Array<FileObject>) => void,
70
- onRejectedFilesDrop?: (fileRejections: Array<FileObject>) => void,
77
+ onValidFilesDrop?: (acceptedFiles: Array<FileObject>) => mixed,
78
+ onRejectedFilesDrop?: (fileRejections: Array<FileObject>) => mixed,
71
79
 
72
80
  // File clear callbacks
73
- onFileClear?: (id: string) => void,
74
- onClear?: () => void,
81
+ onFileClear?: (id: string) => mixed,
82
+ onClear?: () => mixed,
75
83
  };
76
84
 
77
85
  export type FileUploadProps = {
@@ -83,9 +91,10 @@ export type FileUploadProps = {
83
91
  draggingInstruction?: React.Node,
84
92
  secondaryInstruction?: React.Node,
85
93
  required?: boolean,
94
+ handleFileDeletionExternally?: boolean,
86
95
 
87
96
  // File refresh callback
88
- onFileRefreshClick?: (file: FileObject) => void,
97
+ onFileRefreshClick?: (file: FileObject) => mixed,
89
98
  };
90
99
 
91
100
  const FileUploadBase = (props: FileUploadProps, ref) => {
@@ -105,6 +114,7 @@ const FileUploadBase = (props: FileUploadProps, ref) => {
105
114
  onFileClear,
106
115
  onFileRefreshClick,
107
116
  maxFiles = 1,
117
+ handleFileDeletionExternally,
108
118
  } = props;
109
119
 
110
120
  // Get file upload state from useFileUpload hook
@@ -135,6 +145,7 @@ const FileUploadBase = (props: FileUploadProps, ref) => {
135
145
  moveFileToProgress,
136
146
  moveFileToSuccess,
137
147
  moveFileToReject,
148
+ handleFileClear,
138
149
  setShowReUpload,
139
150
  validFiles,
140
151
  rejectedFiles,
@@ -183,7 +194,9 @@ const FileUploadBase = (props: FileUploadProps, ref) => {
183
194
  <FileBlock
184
195
  fileObject={fileObject}
185
196
  onFileRefreshClick={onFileRefreshClick}
186
- handleFileClear={handleFileClear}
197
+ handleFileClear={
198
+ handleFileDeletionExternally ? onFileClear : handleFileClear
199
+ }
187
200
  />
188
201
  </React.Fragment>
189
202
  ))}
@@ -63,6 +63,8 @@ const Link = /*#__PURE__*/React.forwardRef((_ref, ref) => {
63
63
  underline = true,
64
64
  tabIndex = 0,
65
65
  disabled,
66
+ onClick,
67
+ linkComponent: LinkComponent = DefaultLink,
66
68
  ...props
67
69
  } = _ref;
68
70
  const linkRef = React.useRef(null);
@@ -72,14 +74,48 @@ const Link = /*#__PURE__*/React.forwardRef((_ref, ref) => {
72
74
  linkRef.current?.blur();
73
75
  }
74
76
  }, [disabled]);
75
- return /*#__PURE__*/React.createElement("a", _extends({}, props, {
77
+ const handleClick = event => {
78
+ if (disabled) {
79
+ event.preventDefault();
80
+ return;
81
+ }
82
+ onClick?.(event);
83
+ };
84
+
85
+ /**
86
+ * By spec anchor tag wont call onClick on enter key press when the element is focussed
87
+ * as a workaround we would need to listen to key press event and call onClick
88
+ * manually, one workaround to avoid this is to have empty href along with onClick
89
+ * but that would break accessibility
90
+ */
91
+ const handleKeyPress = event => {
92
+ if (event.key === 'Enter' && onClick) {
93
+ handleClick(event);
94
+ }
95
+ };
96
+ return /*#__PURE__*/React.createElement(LinkComponent, _extends({}, props, {
76
97
  tabIndex: disabled ? -1 : tabIndex,
77
98
  ref: linkRef,
78
99
  "data-testid": "Link",
79
100
  className: (0, _classify.default)(_typographyModule.default.link, _typographyModule.default[as], _typographyModule.default[color], {
80
101
  [_typographyModule.default.underline]: underline,
81
102
  [_typographyModule.default.disabled]: disabled
82
- }, className)
103
+ }, className),
104
+ onClick: handleClick,
105
+ onKeyPress: handleKeyPress
83
106
  }), children);
84
107
  });
85
- exports.Link = Link;
108
+ exports.Link = Link;
109
+ const DefaultLink = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
110
+ let {
111
+ children,
112
+ href,
113
+ to,
114
+ ...props
115
+ } = _ref2;
116
+ const resolvedHref = to ?? href;
117
+ return /*#__PURE__*/React.createElement("a", _extends({}, props, {
118
+ href: resolvedHref,
119
+ ref: ref
120
+ }), children);
121
+ });
@@ -57,23 +57,52 @@ export const ANCHOR_TARGET = Object.freeze({
57
57
 
58
58
  export type AnchorTarget = $Values<typeof ANCHOR_TARGET>;
59
59
 
60
- export type LinkProps = {
61
- color?: ColorTypes,
60
+ export type BaseLinkProps = {
62
61
  children: React.Node,
62
+ onClick?: ?(SyntheticEvent<HTMLElement>) => mixed,
63
+ tabIndex?: number,
64
+ disabled?: boolean,
63
65
  className?: string,
64
66
  as?: LinkAs,
65
67
  rel?: AnchorRel,
66
- underline?: boolean,
67
68
  target?: AnchorTarget,
69
+ /**
70
+ * IMPORTANT: If you are using `to` make sure to provide link component from your router
71
+ * if you want to prevent full page reloads in a Single Page Application (SPA).
72
+ *
73
+ * Using `href` in anchor tags causes the browser to navigate to a new URL,
74
+ * resulting in a full page reload. However, in a Single Page Application (SPA), we aim to provide a seamless
75
+ * user experience without such reloads.
76
+ *
77
+ * To achieve client-side navigation and prevent page reloads, use client-side routing libraries
78
+ * (e.g., React Router) and their navigation components (e.g., <Link> or <a> with an onClick handler)
79
+ * to handle navigation within your SPA. These components work without triggering full page reloads
80
+ * and maintain the SPA's performance and user experience.
81
+ *
82
+ */
83
+ to?: string,
68
84
  href?: string,
69
- onClick?: ?(SyntheticEvent<HTMLElement>) => mixed,
70
- tabIndex?: number,
71
- disabled?: boolean,
72
85
  ...
73
86
  };
74
87
 
75
- export const Link: React$AbstractComponent<LinkProps, HTMLAnchorElement> =
76
- React.forwardRef<LinkProps, HTMLAnchorElement>(
88
+ export type LinkProps = {
89
+ ...BaseLinkProps,
90
+ color?: ColorTypes,
91
+ underline?: boolean,
92
+ /**
93
+ * Provide your router's link component
94
+ *
95
+ * import {Link} from 'src/rerouter';
96
+ * import {Link as GenesisLink} from '@spaced-out/ui-design-system/lib/components/Link';
97
+ *
98
+ * <GenesisLink linkComponent={Link} to="/pages" />
99
+ */
100
+ linkComponent?: React.AbstractComponent<BaseLinkProps, ?HTMLAnchorElement>,
101
+ ...
102
+ };
103
+
104
+ export const Link: React$AbstractComponent<LinkProps, ?HTMLAnchorElement> =
105
+ React.forwardRef<LinkProps, ?HTMLAnchorElement>(
77
106
  (
78
107
  {
79
108
  color = TEXT_COLORS.clickable,
@@ -83,6 +112,8 @@ export const Link: React$AbstractComponent<LinkProps, HTMLAnchorElement> =
83
112
  underline = true,
84
113
  tabIndex = 0,
85
114
  disabled,
115
+ onClick,
116
+ linkComponent: LinkComponent = DefaultLink,
86
117
  ...props
87
118
  }: LinkProps,
88
119
  ref,
@@ -95,8 +126,29 @@ export const Link: React$AbstractComponent<LinkProps, HTMLAnchorElement> =
95
126
  }
96
127
  }, [disabled]);
97
128
 
129
+ const handleClick = (event: SyntheticEvent<HTMLElement>) => {
130
+ if (disabled) {
131
+ event.preventDefault();
132
+ return;
133
+ }
134
+
135
+ onClick?.(event);
136
+ };
137
+
138
+ /**
139
+ * By spec anchor tag wont call onClick on enter key press when the element is focussed
140
+ * as a workaround we would need to listen to key press event and call onClick
141
+ * manually, one workaround to avoid this is to have empty href along with onClick
142
+ * but that would break accessibility
143
+ */
144
+ const handleKeyPress = (event) => {
145
+ if (event.key === 'Enter' && onClick) {
146
+ handleClick(event);
147
+ }
148
+ };
149
+
98
150
  return (
99
- <a
151
+ <LinkComponent
100
152
  {...props}
101
153
  tabIndex={disabled ? -1 : tabIndex}
102
154
  ref={linkRef}
@@ -111,9 +163,23 @@ export const Link: React$AbstractComponent<LinkProps, HTMLAnchorElement> =
111
163
  },
112
164
  className,
113
165
  )}
166
+ onClick={handleClick}
167
+ onKeyPress={handleKeyPress}
114
168
  >
115
169
  {children}
116
- </a>
170
+ </LinkComponent>
117
171
  );
118
172
  },
119
173
  );
174
+
175
+ const DefaultLink = React.forwardRef<BaseLinkProps, HTMLAnchorElement>(
176
+ ({children, href, to, ...props}, ref) => {
177
+ const resolvedHref = to ?? href;
178
+
179
+ return (
180
+ <a {...props} href={resolvedHref} ref={ref}>
181
+ {children}
182
+ </a>
183
+ );
184
+ },
185
+ );
@@ -47,6 +47,17 @@ Object.keys(_Banner).forEach(function (key) {
47
47
  }
48
48
  });
49
49
  });
50
+ var _Breadcrumbs = require("./Breadcrumbs");
51
+ Object.keys(_Breadcrumbs).forEach(function (key) {
52
+ if (key === "default" || key === "__esModule") return;
53
+ if (key in exports && exports[key] === _Breadcrumbs[key]) return;
54
+ Object.defineProperty(exports, key, {
55
+ enumerable: true,
56
+ get: function () {
57
+ return _Breadcrumbs[key];
58
+ }
59
+ });
60
+ });
50
61
  var _Button = require("./Button");
51
62
  Object.keys(_Button).forEach(function (key) {
52
63
  if (key === "default" || key === "__esModule") return;
@@ -4,6 +4,7 @@ export * from './Avatar';
4
4
  export * from './AvatarGroup';
5
5
  export * from './Badge';
6
6
  export * from './Banner';
7
+ export * from './Breadcrumbs';
7
8
  export * from './Button';
8
9
  export * from './ButtonDropdown';
9
10
  export * from './ButtonTabs';
@@ -21,7 +21,8 @@ const range = (start, end) => {
21
21
  }, (_, i) => start + i);
22
22
  };
23
23
  exports.range = range;
24
- const convertFileSize = fileSize => {
24
+ const convertFileSize = function () {
25
+ let fileSize = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
25
26
  let sizeInBytes = fileSize;
26
27
  // Check if the file size is less than 1024
27
28
  if (sizeInBytes < 0) {
@@ -15,7 +15,7 @@ export const range = (start: number, end: number): Array<number> => {
15
15
  return Array.from({length}, (_, i) => start + i);
16
16
  };
17
17
 
18
- export const convertFileSize = (fileSize: number): string => {
18
+ export const convertFileSize = (fileSize: number = 0): string => {
19
19
  let sizeInBytes = fileSize;
20
20
  // Check if the file size is less than 1024
21
21
  if (sizeInBytes < 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spaced-out/ui-design-system",
3
- "version": "0.1.48",
3
+ "version": "0.1.50",
4
4
  "main": "index.js",
5
5
  "description": "Sense UI components library",
6
6
  "author": {