agroptima-design-system 0.28.0-beta.13 → 0.28.0-beta.2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agroptima-design-system",
3
- "version": "0.28.0-beta.13",
3
+ "version": "0.28.0-beta.2",
4
4
  "scripts": {
5
5
  "dev": "npm run storybook",
6
6
  "storybook": "storybook dev -p 6006 --ci",
@@ -22,19 +22,19 @@
22
22
  "sass": "^1.83.1"
23
23
  },
24
24
  "devDependencies": {
25
- "@chromatic-com/storybook": "^3.2.4",
25
+ "@chromatic-com/storybook": "^3.2.3",
26
26
  "@eslint/eslintrc": "^3.2.0",
27
27
  "@eslint/js": "^9.16.0",
28
- "@storybook/addon-a11y": "^8.5.1",
28
+ "@storybook/addon-a11y": "^8.4.7",
29
29
  "@storybook/addon-designs": "^8.0.4",
30
- "@storybook/addon-essentials": "^8.5.1",
31
- "@storybook/addon-interactions": "^8.5.1",
32
- "@storybook/addon-links": "^8.5.1",
33
- "@storybook/addon-viewport": "^8.5.1",
34
- "@storybook/blocks": "^8.5.1",
35
- "@storybook/nextjs": "^8.5.1",
36
- "@storybook/react": "^8.5.1",
37
- "@storybook/test": "^8.5.1",
30
+ "@storybook/addon-essentials": "^8.4.7",
31
+ "@storybook/addon-interactions": "^8.4.7",
32
+ "@storybook/addon-links": "^8.4.7",
33
+ "@storybook/addon-viewport": "^8.4.7",
34
+ "@storybook/blocks": "^8.4.7",
35
+ "@storybook/nextjs": "^8.4.7",
36
+ "@storybook/react": "^8.4.7",
37
+ "@storybook/test": "^8.4.7",
38
38
  "@svgr/webpack": "^8.1.0",
39
39
  "@testing-library/jest-dom": "^6.6.3",
40
40
  "@testing-library/react": "^16.1.0",
@@ -55,7 +55,7 @@
55
55
  "jest-axe": "^9.0.0",
56
56
  "jest-environment-jsdom": "^29.7.0",
57
57
  "prettier": "^3.4.2",
58
- "storybook": "^8.5.1",
58
+ "storybook": "^8.4.7",
59
59
  "ts-node": "^10.9.2",
60
60
  "typescript": "^5.7.2"
61
61
  },
@@ -13,7 +13,7 @@ a.card {
13
13
  .card {
14
14
  display: flex;
15
15
  flex-direction: column;
16
- gap: config.$space-2x;
16
+ gap: config.$space-1x;
17
17
  padding: config.$space-3x;
18
18
  width: 100%;
19
19
  p {
@@ -27,12 +27,17 @@ a.card {
27
27
  .icon {
28
28
  width: config.$icon-size-4x;
29
29
  height: config.$icon-size-4x;
30
+ > svg {
31
+ width: 100%;
32
+ height: 100%;
33
+ }
30
34
  }
31
35
 
32
36
  .header {
33
37
  display: flex;
34
38
  flex-direction: row;
35
39
  justify-content: space-between;
40
+ margin-bottom: config.$space-1x;
36
41
  gap: config.$space-1x;
37
42
  .title {
38
43
  overflow: hidden;
@@ -47,7 +52,7 @@ a.card {
47
52
  }
48
53
 
49
54
  .content {
50
- margin-bottom: config.$space-1x;
55
+ margin-bottom: config.$space-2x;
51
56
  }
52
57
 
53
58
  .footer {
@@ -13,8 +13,6 @@ export interface CardProps {
13
13
  isActive?: boolean
14
14
  error?: boolean
15
15
  href?: string
16
- onClick?: () => void
17
- role?: string
18
16
  children: React.ReactNode
19
17
  }
20
18
 
@@ -35,7 +33,6 @@ export function Card({
35
33
  disabled: isDisabled,
36
34
  active: isActive,
37
35
  error: error,
38
- clickable: Boolean(props.onClick),
39
36
  })
40
37
 
41
38
  if (href && !isDisabled) {
@@ -4,7 +4,7 @@ import { classNames } from '../../utils/classNames'
4
4
 
5
5
  export interface CardHeaderProps extends React.ComponentPropsWithoutRef<'div'> {
6
6
  title: string
7
- isBold?: boolean
7
+ isBold: boolean
8
8
  }
9
9
 
10
10
  export function CardHeader({
@@ -1,36 +1,75 @@
1
- @use '../../settings/mixins';
2
1
  @use '../../settings/color_alias';
3
2
  @use '../../settings/typography/cards_table' as typography;
4
3
  @use '../../settings/config';
5
4
  @use '../../settings/breakpoints';
6
5
 
7
- .cards-table-list {
8
- border-collapse: separate;
9
- border-spacing: 0 config.$space-2x;
10
- width: 100%;
6
+ @mixin align-data-vertically() {
7
+ thead {
8
+ display: none;
9
+ }
10
+
11
+ tbody {
12
+ tr {
13
+ flex-direction: row;
14
+ flex-wrap: wrap;
15
+ position: relative;
16
+ gap: config.$space-1x;
17
+ padding: config.$space-2x config.$space-3x;
18
+ }
19
+ }
11
20
 
12
- th,
13
21
  td {
14
- padding: config.$space-2x config.$space-3x;
15
- padding: config.$space-2x config.$space-3x;
22
+ width: 100%;
23
+ flex: inherit;
24
+ padding: 0px;
16
25
  }
26
+ }
27
+
28
+ .cards-table-list {
29
+ display: flex;
30
+ flex-direction: column;
31
+ gap: config.$space-3x;
17
32
 
18
33
  tbody {
34
+ display: flex;
35
+ flex-direction: column;
36
+ gap: config.$space-3x;
37
+
19
38
  tr {
20
39
  box-shadow: 0px 0px 3px 0px rgba(0, 0, 0, 0.25);
21
- cursor: default;
22
40
  }
23
41
  }
24
42
 
25
- .cell {
43
+ tr {
26
44
  display: flex;
45
+ flex-grow: 1;
46
+ }
47
+
48
+ .container {
49
+ display: flex;
50
+ flex-direction: row;
51
+ justify-content: space-between;
27
52
  align-items: center;
28
- gap: config.$space-2x;
53
+ width: 100%;
29
54
  }
30
55
 
31
- .actions > .cell {
32
- gap: config.$space-7x;
33
- justify-content: center;
56
+ th {
57
+ padding: config.$space-2x config.$space-3x;
58
+ text-align: left;
59
+
60
+ &.sortable {
61
+ cursor: pointer;
62
+ }
63
+
64
+ .icon {
65
+ width: config.$icon-size-3x;
66
+ height: config.$icon-size-3x;
67
+ margin-left: config.$space-1x;
68
+ > svg {
69
+ width: 100%;
70
+ height: 100%;
71
+ }
72
+ }
34
73
  }
35
74
 
36
75
  .no-wrap {
@@ -41,14 +80,15 @@
41
80
  justify-content: flex-start;
42
81
  }
43
82
 
44
- .alignment-right {
45
- justify-content: flex-end !important;
46
- }
47
-
48
83
  .alignment-center {
49
84
  justify-content: center;
50
85
  }
51
86
 
87
+ .actions {
88
+ display: flex;
89
+ gap: config.$space-7x;
90
+ }
91
+
52
92
  &.primary {
53
93
  thead > tr {
54
94
  @include typography.cards-table-list-header;
@@ -56,7 +96,37 @@
56
96
 
57
97
  th {
58
98
  background: color_alias.$neutral-color-900;
59
- @include mixins.icon-color(color_alias.$neutral-white);
99
+
100
+ .icon {
101
+ > svg {
102
+ fill: color_alias.$neutral-white;
103
+ path {
104
+ fill: color_alias.$neutral-white;
105
+ }
106
+ }
107
+
108
+ &.ascending {
109
+ > svg {
110
+ .sorter_svg__up {
111
+ fill: color_alias.$primary-color-600;
112
+ }
113
+ .sorter_svg__down {
114
+ fill: color_alias.$neutral-white;
115
+ }
116
+ }
117
+ }
118
+
119
+ &.descending {
120
+ > svg {
121
+ .sorter_svg__up {
122
+ fill: color_alias.$neutral-white;
123
+ }
124
+ .sorter_svg__down {
125
+ fill: color_alias.$primary-color-600;
126
+ }
127
+ }
128
+ }
129
+ }
60
130
  }
61
131
 
62
132
  th:first-child {
@@ -84,74 +154,77 @@
84
154
 
85
155
  tr.disabled {
86
156
  background: color_alias.$neutral-color-50;
157
+
87
158
  td {
88
159
  @include typography.cards-table-list-disabled-text;
89
160
  }
90
161
  }
91
-
92
- tr > td {
93
- border-top: 1px solid transparent;
94
- border-bottom: 1px solid transparent;
95
- &:first-child {
96
- border-left: 1px solid transparent;
97
- }
98
- &:last-child {
99
- border-right: 1px solid transparent;
100
- }
101
- }
102
-
103
162
  tr.active {
163
+ border-color: color_alias.$primary-color-1000;
104
164
  box-shadow: none;
105
- > td {
106
- border-color: color_alias.$primary-color-1000;
107
- }
165
+ }
166
+ tr.action {
167
+ cursor: default;
108
168
  }
109
169
  }
110
170
 
111
- @media only screen and (max-width: breakpoints.$large) {
112
- thead {
113
- display: none;
114
- }
171
+ // Desktop
172
+ thead {
173
+ display: flex;
174
+ }
175
+ tr {
176
+ flex-direction: row;
177
+ }
115
178
 
116
- tr {
117
- display: flex;
118
- flex-direction: row;
119
- flex-wrap: wrap;
120
- position: relative;
121
- margin-block: config.$space-3x;
122
- padding: config.$space-2x config.$space-3x;
123
- gap: config.$space-1x;
124
- }
179
+ th,
180
+ td {
181
+ display: flex;
182
+ justify-content: flex-start;
183
+ align-items: center;
184
+ flex: 2;
125
185
 
126
- td {
127
- width: 100%;
128
- flex: inherit;
129
- padding: 0px;
186
+ .checkbox-group {
187
+ margin-right: config.$space-1x;
130
188
  }
189
+ }
131
190
 
132
- .actions {
133
- width: auto;
134
- position: absolute;
135
- inset: config.$space-3x config.$space-3x auto auto;
136
- }
191
+ td {
192
+ padding: config.$space-2x config.$space-3x;
193
+ }
137
194
 
138
- .floating-left-mobile {
139
- width: auto;
140
- order: 98;
141
- }
195
+ th.actions {
196
+ justify-content: center;
197
+ }
142
198
 
143
- .floating-right-mobile {
144
- margin-left: auto;
145
- width: auto;
146
- order: 99;
199
+ td.actions {
200
+ order: 0;
201
+ justify-content: center;
202
+ }
203
+
204
+ .badge:has(.icon) {
205
+ margin: auto;
206
+ }
207
+
208
+ .alignment-right {
209
+ justify-content: flex-end !important;
210
+ }
211
+
212
+ &.vertically {
213
+ @include align-data-vertically();
214
+ }
215
+
216
+ // Media queries
217
+ // Mobile & tablet cases
218
+ @media only screen and (max-width: breakpoints.$large) {
219
+ @include align-data-vertically();
220
+
221
+ td:first-child {
222
+ order: -2;
147
223
  }
148
224
 
149
- tr.active {
150
- box-shadow: none;
151
- border: 1px solid color_alias.$primary-color-1000;
152
- > td {
153
- border-color: transparent !important;
154
- }
225
+ .badge:has(.icon) {
226
+ margin: 0;
227
+ margin-left: config.$space-2x;
155
228
  }
156
229
 
157
230
  &.with-title td:not(.actions):nth-child(2) {
@@ -173,5 +246,39 @@
173
246
  .title-actions-1 {
174
247
  width: calc(100% - 1 * config.$icon-size-5x - 8px);
175
248
  }
249
+
250
+ td.actions {
251
+ order: -1;
252
+ flex-grow: 1;
253
+ align-items: flex-start;
254
+ justify-content: flex-end;
255
+ flex-basis: content;
256
+ }
257
+
258
+ .alignment-right {
259
+ justify-content: flex-start;
260
+ }
261
+
262
+ .badge {
263
+ position: absolute;
264
+ inset: auto auto config.$space-2x config.$space-2x;
265
+ }
266
+
267
+ .badge:has(> .icon) {
268
+ position: relative;
269
+ inset: inherit;
270
+ }
271
+
272
+ td:has(.badge > .icon) {
273
+ justify-content: flex-start;
274
+ }
275
+
276
+ // Specify Badge vertical position depending on having a right aligned item or not
277
+ tr:not(.alignment-right) {
278
+ padding-bottom: config.$space-10x;
279
+ }
280
+ tr:has(.alignment-right) {
281
+ padding-bottom: config.$space-2x;
282
+ }
176
283
  }
177
284
  }
@@ -7,6 +7,7 @@ export interface CardsTableProps
7
7
  extends React.ComponentPropsWithoutRef<'table'> {
8
8
  variant?: Variant
9
9
  withTitle?: boolean
10
+ vertically?: boolean
10
11
  }
11
12
 
12
13
  export function CardsTable({
@@ -14,11 +15,13 @@ export function CardsTable({
14
15
  summary,
15
16
  variant = 'primary',
16
17
  withTitle = false,
18
+ vertically = false,
17
19
  children,
18
20
  ...props
19
21
  }: CardsTableProps): React.JSX.Element {
20
22
  const cssClasses = classNames('cards-table-list', variant, className, {
21
23
  'with-title': withTitle,
24
+ vertically: vertically,
22
25
  })
23
26
  return (
24
27
  <table summary={summary} role="table" className={cssClasses} {...props}>
@@ -13,8 +13,6 @@ export interface CardsTableCellProps
13
13
  noWrap?: boolean
14
14
  align?: Alignment
15
15
  actions?: boolean
16
- floatingLeftMobile?: boolean
17
- floatingRightMobile?: boolean
18
16
  titleWithActions?: number
19
17
  }
20
18
 
@@ -23,8 +21,6 @@ export function CardsTableCell({
23
21
  actions = false,
24
22
  titleWithActions = 0,
25
23
  align = Alignment.Left,
26
- floatingLeftMobile = false,
27
- floatingRightMobile = false,
28
24
  children,
29
25
  className,
30
26
  ...props
@@ -38,25 +34,12 @@ export function CardsTableCell({
38
34
  className,
39
35
  {
40
36
  'no-wrap': noWrap,
37
+ actions,
41
38
  },
42
39
  )
43
-
44
- const actionsStopPropagation = actions
45
- ? { onClick: (e: React.MouseEvent) => e.stopPropagation() }
46
- : {}
47
-
48
40
  return (
49
- <td
50
- role="cell"
51
- className={classNames({
52
- actions,
53
- 'floating-left-mobile': floatingLeftMobile,
54
- 'floating-right-mobile': floatingRightMobile,
55
- })}
56
- {...actionsStopPropagation}
57
- {...props}
58
- >
59
- <div className={cssClasses}>{children}</div>
41
+ <td role="cell" className={cssClasses} {...props}>
42
+ {children}
60
43
  </td>
61
44
  )
62
45
  }
@@ -14,18 +14,12 @@ export function CardsTableHeader({
14
14
  actions = false,
15
15
  ...props
16
16
  }: CardsTableHeaderProps) {
17
- const cssClasses = classNames(
18
- 'header',
19
- 'cell',
20
- `alignment-${align}`,
21
- className,
22
- {
23
- actions,
24
- },
25
- )
17
+ const cssClasses = classNames('header', `alignment-${align}`, className, {
18
+ actions,
19
+ })
26
20
  return (
27
- <th role="columnheader" {...props}>
28
- <div className={cssClasses}>{children}</div>
21
+ <th scope="col" role="columnheader" className={cssClasses} {...props}>
22
+ {children}
29
23
  </th>
30
24
  )
31
25
  }
@@ -20,6 +20,7 @@ export function CardsTableRow({
20
20
  className={classNames('row', {
21
21
  disabled,
22
22
  active,
23
+ action: Boolean(props.onClick),
23
24
  })}
24
25
  {...props}
25
26
  >
@@ -0,0 +1,127 @@
1
+ import './CardsTable/CardsTable.scss'
2
+ import React, { useState } from 'react'
3
+ import { sortBy } from '../utils/sort'
4
+ import {
5
+ CardsTable,
6
+ CardsTableBody,
7
+ CardsTableCell,
8
+ CardsTableHead,
9
+ CardsTableHeader,
10
+ CardsTableRow,
11
+ } from './CardsTable'
12
+ import type { IconType } from './Icon'
13
+ import { Icon } from './Icon'
14
+
15
+ export type Variant = 'primary'
16
+
17
+ export enum Order {
18
+ Ascending = 'ascending',
19
+ Descending = 'descending',
20
+ None = 'none',
21
+ }
22
+
23
+ export type Header = {
24
+ label: string
25
+ icon?: IconType
26
+ columnId: string
27
+ isSortable?: boolean
28
+ }
29
+
30
+ export type Column = {
31
+ [key: string]: string
32
+ }
33
+ export type Row = { id: string; isDisabled?: boolean; columns: Column }
34
+
35
+ export type SortState = { columnId: string; order: Order }
36
+
37
+ export interface CardsTableListProps
38
+ extends React.ComponentPropsWithoutRef<'table'> {
39
+ headers: Header[]
40
+ rows: Row[]
41
+ variant?: Variant
42
+ }
43
+
44
+ export function CardsTableList({
45
+ headers,
46
+ rows,
47
+ summary,
48
+ variant = 'primary',
49
+ }: CardsTableListProps): React.JSX.Element {
50
+ const [sortState, setSortState] = useState<SortState | null>(() => {
51
+ return headers[0]?.isSortable
52
+ ? { columnId: headers[0].columnId, order: Order.Ascending }
53
+ : null
54
+ })
55
+
56
+ function checkColumnOrder(columnId: string) {
57
+ if (sortState?.columnId === columnId) {
58
+ return sortState.order
59
+ }
60
+ return Order.None
61
+ }
62
+
63
+ function applySort(columnId: string) {
64
+ if (!headers.find((header) => header.columnId === columnId)?.isSortable) {
65
+ return
66
+ }
67
+
68
+ setSortState({
69
+ columnId,
70
+ order:
71
+ checkColumnOrder(columnId) === Order.Ascending
72
+ ? Order.Descending
73
+ : Order.Ascending,
74
+ })
75
+ }
76
+ const sortedRows = sortState?.columnId
77
+ ? sortBy({
78
+ rows,
79
+ prop: sortState?.columnId,
80
+ order: sortState?.order,
81
+ })
82
+ : rows
83
+
84
+ return (
85
+ <CardsTable summary={summary} variant={variant}>
86
+ <CardsTableHead>
87
+ <CardsTableRow>
88
+ {headers.map((header) => (
89
+ <CardsTableHeader
90
+ key={header.columnId}
91
+ aria-sort={checkColumnOrder(header.columnId)}
92
+ className={header.isSortable ? 'sortable' : ''}
93
+ onClick={() => applySort(header.columnId)}
94
+ >
95
+ <div className="container">
96
+ <div>
97
+ <span>{header.label}</span>
98
+ {header.icon && <Icon name={header.icon} />}
99
+ </div>
100
+ {header.isSortable && (
101
+ <Icon
102
+ name="Sorter"
103
+ className={checkColumnOrder(header.columnId)}
104
+ />
105
+ )}
106
+ </div>
107
+ </CardsTableHeader>
108
+ ))}
109
+ </CardsTableRow>
110
+ </CardsTableHead>
111
+ <CardsTableBody>
112
+ {sortedRows.map((row: Row) => {
113
+ const cells = Object.entries(row.columns)
114
+ return (
115
+ <CardsTableRow key={row.id} isDisabled={row.isDisabled}>
116
+ {cells.map(([columnId, value]) => (
117
+ <CardsTableCell key={`${row.id}${columnId}`}>
118
+ {value}
119
+ </CardsTableCell>
120
+ ))}
121
+ </CardsTableRow>
122
+ )
123
+ })}
124
+ </CardsTableBody>
125
+ </CardsTable>
126
+ )
127
+ }
@@ -42,12 +42,7 @@ export function Checkbox({
42
42
  aria-label={accessibilityLabel || label}
43
43
  {...props}
44
44
  />
45
- <label
46
- htmlFor={identifier}
47
- className={classNames({ 'visually-hidden': hideLabel })}
48
- >
49
- {label}
50
- </label>
45
+ {!hideLabel && <label htmlFor={identifier}>{label}</label>}
51
46
  </div>
52
47
  )
53
48
  }
@@ -4,12 +4,8 @@
4
4
  @use '../settings/depth';
5
5
  @use '../settings/breakpoints';
6
6
 
7
- // Interpolation applied: https://sass-lang.com/documentation/breaking-changes/css-vars/
8
-
9
7
  .date-picker {
10
8
  .rdp-root {
11
- width: 309px;
12
- --rdp-accent-color: #{color_alias.$primary-color-600};
13
- --rdp-accent-background-color: #{color_alias.$primary-color-50};
9
+ width: 320px;
14
10
  }
15
11
  }