@iota/apps-ui-kit 0.2.1 → 0.5.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 (43) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +16 -0
  3. package/dist/index.d.ts +91 -15
  4. package/dist/index.es.js +4536 -4297
  5. package/dist/index.es.js.map +1 -1
  6. package/dist/index.umd.js +52 -52
  7. package/dist/index.umd.js.map +1 -1
  8. package/dist/style.css +1 -1
  9. package/package.json +3 -3
  10. package/src/lib/components/atoms/badge/badge.classes.ts +9 -0
  11. package/src/lib/components/atoms/badge/badge.enums.ts +3 -0
  12. package/src/lib/components/atoms/button/button.classes.ts +4 -4
  13. package/src/lib/components/atoms/index.ts +1 -0
  14. package/src/lib/components/atoms/info-box/InfoBox.tsx +1 -1
  15. package/src/lib/components/atoms/info-box/infoBox.classes.ts +7 -2
  16. package/src/lib/components/atoms/info-box/infoBox.enums.ts +2 -0
  17. package/src/lib/components/atoms/snackbar/snackbar.classes.ts +5 -1
  18. package/src/lib/components/atoms/snackbar/snackbar.enums.tsx +2 -0
  19. package/src/lib/components/atoms/toggle/Toggle.tsx +140 -0
  20. package/src/lib/components/atoms/toggle/index.ts +6 -0
  21. package/src/lib/components/atoms/toggle/toggle.classes.ts +41 -0
  22. package/src/lib/components/atoms/toggle/toggle.enums.ts +12 -0
  23. package/src/lib/components/atoms/tooltip/Tooltip.tsx +4 -1
  24. package/src/lib/components/atoms/visual-asset-card/VisualAssetCard.tsx +26 -18
  25. package/src/lib/components/molecules/address/Address.tsx +1 -1
  26. package/src/lib/components/molecules/card/CardAction.tsx +4 -2
  27. package/src/lib/components/molecules/card/CardBody.tsx +12 -1
  28. package/src/lib/components/molecules/card/card.classes.ts +1 -0
  29. package/src/lib/components/molecules/card/card.enums.ts +1 -0
  30. package/src/lib/components/molecules/input/input.classes.ts +2 -2
  31. package/src/lib/components/molecules/search/Search.tsx +39 -5
  32. package/src/lib/components/molecules/table-header-cell/TableHeaderCell.tsx +27 -13
  33. package/src/lib/components/molecules/title/Title.tsx +9 -1
  34. package/src/lib/components/organisms/accordion/Accordion.tsx +3 -5
  35. package/src/lib/components/organisms/dialog/Dialog.tsx +6 -5
  36. package/src/lib/components/organisms/navbar/Navbar.tsx +10 -10
  37. package/src/lib/components/organisms/table/Table.tsx +19 -3
  38. package/src/lib/constants/colors.constants.ts +6 -0
  39. package/src/lib/styles/index.css +13 -0
  40. package/src/lib/tailwind/base.presets.ts +2 -0
  41. package/src/lib/tailwind/plugins.ts +8 -0
  42. package/src/storybook/stories/atoms/Toggle.stories.tsx +59 -0
  43. package/src/storybook/stories/organisms/Navbar.stories.tsx +2 -2
@@ -1,7 +1,6 @@
1
1
  // Copyright (c) 2024 IOTA Stiftung
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { useState } from 'react';
5
4
  import { SortByDown, SortByUp } from '@iota/apps-ui-icons';
6
5
  import cx from 'classnames';
7
6
  import { Checkbox } from '@/lib';
@@ -52,6 +51,10 @@ export interface TableHeaderCellProps {
52
51
  * Whether the cell content should be centered.
53
52
  */
54
53
  isContentCentered?: boolean;
54
+ /**
55
+ * Sort order when cell is initialized
56
+ */
57
+ sortOrder?: TableHeaderCellSortOrder;
55
58
  }
56
59
 
57
60
  export function TableHeaderCell({
@@ -64,17 +67,13 @@ export function TableHeaderCell({
64
67
  isContentCentered,
65
68
  onSortClick,
66
69
  onCheckboxChange,
70
+ sortOrder,
67
71
  }: TableHeaderCellProps): JSX.Element {
68
- const [sortOrder, setSortOrder] = useState<TableHeaderCellSortOrder | null>(
69
- TableHeaderCellSortOrder.Asc,
70
- );
71
-
72
72
  const handleSort = () => {
73
73
  const newSortOrder =
74
74
  sortOrder === TableHeaderCellSortOrder.Asc
75
75
  ? TableHeaderCellSortOrder.Desc
76
76
  : TableHeaderCellSortOrder.Asc;
77
- setSortOrder(newSortOrder);
78
77
  if (onSortClick) {
79
78
  onSortClick(columnKey, newSortOrder);
80
79
  }
@@ -83,10 +82,30 @@ export function TableHeaderCell({
83
82
  const textColorClass = 'text-neutral-10 dark:text-neutral-92';
84
83
  const textSizeClass = 'text-label-lg';
85
84
 
85
+ const sortElement = (() => {
86
+ if (!hasSort) {
87
+ return null;
88
+ }
89
+
90
+ if (sortOrder === TableHeaderCellSortOrder.Asc) {
91
+ return <SortByUp className="shrink-0" />;
92
+ }
93
+
94
+ if (sortOrder === TableHeaderCellSortOrder.Desc) {
95
+ return <SortByDown className="shrink-0" />;
96
+ }
97
+
98
+ return <SortByUp className="invisible shrink-0 group-hover:visible" />;
99
+ })();
100
+
86
101
  return (
87
102
  <th
103
+ onClick={hasSort ? handleSort : undefined}
88
104
  className={cx(
89
- 'state-layer relative h-14 border-b border-shader-neutral-light-8 px-md after:pointer-events-none dark:border-shader-neutral-dark-8',
105
+ 'state-layer group relative h-14 border-b border-shader-neutral-light-8 px-md after:pointer-events-none dark:border-shader-neutral-dark-8',
106
+ {
107
+ 'cursor-pointer': hasSort,
108
+ },
90
109
  )}
91
110
  >
92
111
  <div
@@ -114,12 +133,7 @@ export function TableHeaderCell({
114
133
  {label}
115
134
  </span>
116
135
  )}
117
- {hasSort && sortOrder === TableHeaderCellSortOrder.Asc && (
118
- <SortByUp className="cursor-pointer" onClick={handleSort} />
119
- )}
120
- {hasSort && sortOrder === TableHeaderCellSortOrder.Desc && (
121
- <SortByDown className="cursor-pointer" onClick={handleSort} />
122
- )}
136
+ {sortElement}
123
137
  </div>
124
138
  </th>
125
139
  );
@@ -36,6 +36,10 @@ interface TitleProps {
36
36
  * The size of the component
37
37
  */
38
38
  size?: TitleSize;
39
+ /**
40
+ * The 'data-testid' attribute value (used in e2e tests)
41
+ */
42
+ testId?: string;
39
43
  }
40
44
 
41
45
  export function Title({
@@ -46,9 +50,13 @@ export function Title({
46
50
  supportingElement,
47
51
  tooltipPosition,
48
52
  size = TitleSize.Medium,
53
+ testId,
49
54
  }: TitleProps) {
50
55
  return (
51
- <div className={cx('flex flex-row items-center justify-between', TITLE_PADDINGS[size])}>
56
+ <div
57
+ className={cx('flex flex-row items-center justify-between', TITLE_PADDINGS[size])}
58
+ data-testid={testId}
59
+ >
52
60
  <div className="flex flex-row items-center gap-x-xxxs">
53
61
  <div className="flex flex-col justify-start">
54
62
  <div className="flex flex-row items-center gap-x-0.5 text-neutral-10 dark:text-neutral-92">
@@ -44,16 +44,14 @@ export function AccordionHeader({
44
44
  children,
45
45
  isExpanded,
46
46
  hideArrow,
47
- hideBorder,
48
47
  }: PropsWithChildren<AccordionHeaderProps>) {
49
48
  return (
50
49
  <div
51
50
  onClick={onToggle}
52
51
  className={cx(
53
- 'state-layer relative flex cursor-pointer items-center justify-between gap-md py-sm--rs',
52
+ 'state-layer relative flex cursor-pointer items-center justify-between gap-md overflow-hidden rounded-xl py-sm--rs',
54
53
  {
55
54
  'pr-md--rs': !hideArrow,
56
- 'rounded-xl': hideBorder,
57
55
  },
58
56
  )}
59
57
  >
@@ -100,8 +98,8 @@ export function Accordion({
100
98
  }): React.JSX.Element {
101
99
  return (
102
100
  <div
103
- className={cx('overflow-hidden rounded-xl bg-neutral-100 dark:bg-neutral-6', {
104
- ' border border-shader-neutral-light-8 dark:border-shader-neutral-dark-8':
101
+ className={cx('rounded-xl bg-neutral-100 dark:bg-neutral-6', {
102
+ 'border border-shader-neutral-light-8 dark:border-shader-neutral-dark-8':
105
103
  !hideBorder,
106
104
  })}
107
105
  >
@@ -68,17 +68,18 @@ const DialogContent = React.forwardRef<
68
68
  }, [containerId]);
69
69
  const positionClass =
70
70
  position === DialogPosition.Right
71
- ? 'right-0 h-screen top-0 w-full'
72
- : 'max-h-[60vh] left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-xl';
71
+ ? 'overflow-hidden right-0 h-screen top-0 w-full'
72
+ : 'overflow-y-auto overflow-x-hidden left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-xl';
73
73
  const widthClass =
74
74
  position === DialogPosition.Right ? 'md:w-96 max-w-[500px]' : customWidth;
75
+ const heightClass = position === DialogPosition.Right ? 'h-screen' : 'max-h-[80vh] h-full';
75
76
  return (
76
77
  <RadixDialog.Portal container={containerElement}>
77
78
  <DialogOverlay showCloseIcon={showCloseOnOverlay} position={position} />
78
79
  <RadixDialog.Content
79
80
  ref={ref}
80
81
  className={cx(
81
- 'fixed z-[99999] flex flex-col justify-center overflow-hidden bg-primary-100 dark:bg-neutral-6',
82
+ 'fixed z-[99999] flex flex-col justify-center bg-primary-100 dark:bg-neutral-6',
82
83
  positionClass,
83
84
  widthClass,
84
85
  )}
@@ -88,7 +89,7 @@ const DialogContent = React.forwardRef<
88
89
  <RadixDialog.Title />
89
90
  <RadixDialog.Description />
90
91
  </VisuallyHidden.Root>
91
- {children}
92
+ <div className={cx('flex flex-1 flex-col', heightClass)}>{children}</div>
92
93
  </RadixDialog.Content>
93
94
  </RadixDialog.Portal>
94
95
  );
@@ -112,7 +113,7 @@ const DialogBody = React.forwardRef<React.ElementRef<'div'>, React.ComponentProp
112
113
  (props, ref) => (
113
114
  <div
114
115
  ref={ref}
115
- className="p-md--rs text-body-sm text-neutral-40 dark:text-neutral-60"
116
+ className="flex-1 overflow-y-auto p-md--rs text-body-sm text-neutral-40 dark:text-neutral-60"
116
117
  {...props}
117
118
  />
118
119
  ),
@@ -9,9 +9,9 @@ export type NavbarItemWithId = NavbarItemProps & { id: string };
9
9
 
10
10
  export interface NavbarProps {
11
11
  /**
12
- * If this flag is true we need to leave only the icon and collapsable button
12
+ * If this flag is true we need to leave only the icon and collapsible button
13
13
  */
14
- isCollapsable?: boolean;
14
+ isCollapsible?: boolean;
15
15
 
16
16
  /**
17
17
  * List of elements to be displayed in the navbar.
@@ -29,7 +29,7 @@ export interface NavbarProps {
29
29
  onClickItem: (id: string) => void;
30
30
 
31
31
  /**
32
- * If the navbar is collapsable, this flag indicates if it is open or not.
32
+ * If the navbar is collapsible, this flag indicates if it is open or not.
33
33
  */
34
34
  isOpen?: boolean;
35
35
 
@@ -43,16 +43,16 @@ export function Navbar({
43
43
  items,
44
44
  activeId,
45
45
  onClickItem,
46
- isCollapsable = false,
46
+ isCollapsible = false,
47
47
  onToggleNavbar,
48
48
  }: NavbarProps) {
49
49
  return (
50
50
  <div
51
51
  className={cx('flex h-fit w-full', {
52
- 'flex-col px-md py-xs sm:h-full sm:w-auto sm:px-none sm:py-xl': isCollapsable,
52
+ 'flex-col px-md py-xs sm:h-full sm:w-auto sm:px-none sm:py-xl': isCollapsible,
53
53
  })}
54
54
  >
55
- {isCollapsable && (
55
+ {isCollapsible && (
56
56
  <div className="flex w-full items-center justify-between sm:mb-[48px] sm:flex-col">
57
57
  <div className="flex justify-center [&_svg]:h-[38px] [&_svg]:w-[38px]">
58
58
  <IotaLogoMark className="text-neutral-10 dark:text-neutral-92" />
@@ -67,16 +67,16 @@ export function Navbar({
67
67
  )}
68
68
  <div
69
69
  className={cx({
70
- 'flex w-full justify-between px-sm py-xxs': !isCollapsable,
71
- 'hidden sm:flex sm:flex-col sm:gap-2': isCollapsable,
70
+ 'flex w-full justify-between px-sm py-xxs': !isCollapsible,
71
+ 'hidden sm:flex sm:flex-col sm:gap-2': isCollapsible,
72
72
  })}
73
73
  >
74
74
  {items.map((item) => (
75
75
  <div
76
76
  key={item.id}
77
77
  className={cx('flex items-center', {
78
- 'px-xs py-xxs': !isCollapsable,
79
- 'py-xxs pl-xs pr-sm': isCollapsable,
78
+ 'px-xs py-xxs': !isCollapsible,
79
+ 'py-xxs pl-xs pr-sm': isCollapsible,
80
80
  })}
81
81
  data-testid={`nav-${item.id}`}
82
82
  >
@@ -75,6 +75,11 @@ export type TableProps = {
75
75
  * The page size selector component.
76
76
  */
77
77
  pageSizeSelector?: ReactNode;
78
+
79
+ /**
80
+ * If the table should take the full height of the parent.
81
+ */
82
+ heightFull?: boolean;
78
83
  };
79
84
 
80
85
  export function Table({
@@ -85,12 +90,23 @@ export function Table({
85
90
  rowIndexes,
86
91
  children,
87
92
  pageSizeSelector,
93
+ heightFull = false,
88
94
  }: PropsWithChildren<TableProps>): JSX.Element {
89
95
  return (
90
96
  <TableProvider selectedRowIndexes={selectedRowIndexes} rowIndexes={rowIndexes}>
91
- <div className="w-full">
92
- <div className="overflow-auto">
93
- <table className="w-full table-auto">{children}</table>
97
+ <div className={cx('w-full', { 'h-full': heightFull })}>
98
+ <div
99
+ className={cx('overflow-auto', {
100
+ 'h-full': heightFull,
101
+ })}
102
+ >
103
+ <table
104
+ className={cx('w-full table-auto', {
105
+ 'h-full': heightFull,
106
+ })}
107
+ >
108
+ {children}
109
+ </table>
94
110
  </div>
95
111
  <div
96
112
  className={cx('flex w-full items-center gap-2 pt-md', {
@@ -73,6 +73,12 @@ export const IOTA_PRIMITIVES_COLOR_PALETTE = {
73
73
  '98': '#f8fafc',
74
74
  '100': '#ffffff',
75
75
  },
76
+ warning: {
77
+ '10': '#422C00',
78
+ '20': '#2D1E00',
79
+ '60': '#BE8F32',
80
+ '90': '#FFE3AB',
81
+ },
76
82
  };
77
83
 
78
84
  export const SHADER_COLOR_PALETTE = {
@@ -120,6 +120,19 @@
120
120
  }
121
121
  }
122
122
 
123
+ @-moz-document url-prefix() {
124
+ * {
125
+ scrollbar-width: thin;
126
+ scrollbar-color: theme('colors.neutral.30') transparent;
127
+ }
128
+
129
+ :root:not(.dark) {
130
+ * {
131
+ scrollbar-color: theme('colors.neutral.80') transparent;
132
+ }
133
+ }
134
+ }
135
+
123
136
  @supports selector(::-webkit-scrollbar) {
124
137
  *::-webkit-scrollbar {
125
138
  @apply w-3.5;
@@ -10,10 +10,12 @@ import {
10
10
  OPACITY,
11
11
  generateVariableSpacing,
12
12
  } from './constants';
13
+ import { firefoxPlugin } from './plugins';
13
14
 
14
15
  export const BASE_CONFIG: Config = {
15
16
  content: ['./src/**/*.{html,js,jsx,ts,tsx}'],
16
17
  darkMode: 'class',
18
+ plugins: [firefoxPlugin],
17
19
  theme: {
18
20
  extend: {
19
21
  fontSize: {
@@ -0,0 +1,8 @@
1
+ // Copyright (c) 2024 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import plugin from 'tailwindcss/plugin';
5
+
6
+ export const firefoxPlugin = plugin(({ addVariant }) => {
7
+ addVariant('firefox', '@-moz-document url-prefix()');
8
+ });
@@ -0,0 +1,59 @@
1
+ // Copyright (c) 2024 IOTA Stiftung
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { Meta, StoryObj } from '@storybook/react';
5
+ import { Toggle, ToggleLabelPosition, ToggleSize } from '@/components';
6
+ import { useState } from 'react';
7
+
8
+ const meta: Meta<typeof Toggle> = {
9
+ component: Toggle,
10
+ tags: ['autodocs'],
11
+ argTypes: {
12
+ isToggled: {
13
+ control: { type: 'boolean' },
14
+ },
15
+ label: {
16
+ control: { type: 'text' },
17
+ },
18
+ labelPosition: {
19
+ control: { type: 'select' },
20
+ options: Object.values(ToggleLabelPosition),
21
+ },
22
+ isDisabled: {
23
+ control: { type: 'boolean' },
24
+ },
25
+ size: {
26
+ control: { type: 'select' },
27
+ options: Object.values(ToggleSize),
28
+ },
29
+ onChange: {
30
+ action: 'changed',
31
+ },
32
+ },
33
+ args: {
34
+ isToggled: false,
35
+ isDisabled: false,
36
+ size: ToggleSize.Default,
37
+ labelPosition: ToggleLabelPosition.Right,
38
+ },
39
+ } satisfies Meta<typeof Toggle>;
40
+
41
+ export default meta;
42
+
43
+ type Story = StoryObj<typeof meta>;
44
+
45
+ export const Default: Story = {
46
+ args: {
47
+ label: 'Label',
48
+ },
49
+ render: (args) => {
50
+ const [isToggled, setIsToggled] = useState(args.isToggled);
51
+
52
+ const handleToggleChange = (newState: boolean) => {
53
+ console.log('Toggle state changed:', newState);
54
+ setIsToggled(newState);
55
+ };
56
+
57
+ return <Toggle {...args} isToggled={isToggled} onChange={handleToggleChange} />;
58
+ },
59
+ };
@@ -47,7 +47,7 @@ type Story = StoryObj<typeof meta>;
47
47
 
48
48
  export const Default: Story = {};
49
49
 
50
- export const Collapsable: Story = {
50
+ export const Collapsible: Story = {
51
51
  args: {},
52
52
  argTypes: {},
53
53
  render: (args) => {
@@ -57,7 +57,7 @@ export const Collapsable: Story = {
57
57
  return (
58
58
  <div className="flex h-96">
59
59
  <Navbar
60
- isCollapsable
60
+ isCollapsible
61
61
  items={NAVBAR_ITEMS}
62
62
  activeId={activeId}
63
63
  onClickItem={(id) => setActiveId(id)}