@axinom/mosaic-ui 0.38.0-rc.3 → 0.38.0-rc.5

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 (25) hide show
  1. package/dist/components/DynamicDataList/DynamicDataList.model.d.ts +26 -6
  2. package/dist/components/DynamicDataList/DynamicDataList.model.d.ts.map +1 -1
  3. package/dist/components/DynamicDataList/DynamicListDataEntry/DynamicListDataEntry.d.ts.map +1 -1
  4. package/dist/components/DynamicDataList/DynamicListRow/DynamicListRow.d.ts.map +1 -1
  5. package/dist/components/List/List.model.d.ts +29 -1
  6. package/dist/components/List/List.model.d.ts.map +1 -1
  7. package/dist/components/List/ListHeader/ColumnLabel/ColumnLabel.d.ts +5 -4
  8. package/dist/components/List/ListHeader/ColumnLabel/ColumnLabel.d.ts.map +1 -1
  9. package/dist/components/List/ListRow/ListRow.d.ts.map +1 -1
  10. package/dist/index.es.js +2 -2
  11. package/dist/index.es.js.map +1 -1
  12. package/dist/index.js +2 -2
  13. package/dist/index.js.map +1 -1
  14. package/package.json +3 -3
  15. package/src/components/DynamicDataList/DynamicDataList.model.ts +33 -6
  16. package/src/components/DynamicDataList/DynamicDataList.stories.tsx +15 -1
  17. package/src/components/DynamicDataList/DynamicListDataEntry/DynamicListDataEntry.tsx +6 -3
  18. package/src/components/DynamicDataList/DynamicListHeader/DynamicListHeader.tsx +1 -1
  19. package/src/components/DynamicDataList/DynamicListRow/DynamicListRow.tsx +7 -9
  20. package/src/components/List/List.model.ts +34 -1
  21. package/src/components/List/List.stories.tsx +18 -0
  22. package/src/components/List/List.tsx +1 -1
  23. package/src/components/List/ListHeader/ColumnLabel/ColumnLabel.tsx +11 -8
  24. package/src/components/List/ListHeader/ListHeader.tsx +1 -1
  25. package/src/components/List/ListRow/ListRow.tsx +9 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axinom/mosaic-ui",
3
- "version": "0.38.0-rc.3",
3
+ "version": "0.38.0-rc.5",
4
4
  "description": "UI components for building Axinom Mosaic applications",
5
5
  "author": "Axinom",
6
6
  "license": "PROPRIETARY",
@@ -32,7 +32,7 @@
32
32
  "build-storybook": "storybook build"
33
33
  },
34
34
  "dependencies": {
35
- "@axinom/mosaic-core": "^0.4.11-rc.3",
35
+ "@axinom/mosaic-core": "^0.4.11-rc.5",
36
36
  "@faker-js/faker": "^7.4.0",
37
37
  "@popperjs/core": "^2.9.2",
38
38
  "clsx": "^1.1.0",
@@ -104,5 +104,5 @@
104
104
  "publishConfig": {
105
105
  "access": "public"
106
106
  },
107
- "gitHead": "88fa77642747812f10543b7ee9f0aa1e58d5ecdf"
107
+ "gitHead": "8a9ad3cb8a296d5ef91e45bc07c4f0836f82dead"
108
108
  }
@@ -1,19 +1,42 @@
1
1
  import { Data } from '../../types/data';
2
2
  import { ColumnRenderer } from '../List/List.model';
3
3
 
4
- export interface DynamicListColumn<T extends Data> {
4
+ interface UnboundColumn<T extends Data> extends BaseColumn<T> {
5
+ /** The name of the property on the Data object that should be rendered in that column. */
6
+ propertyName?: keyof T;
7
+ /**
8
+ * A custom render function that will be called for every row(except data entry row) that should be rendered.
9
+ * The DynamicDataList will already try to render the data in a meaningful way, but in some advanced use cases more control is required.
10
+ */
11
+ render: ColumnRenderer<T>;
12
+
13
+ /**
14
+ * The identifier for the column.
15
+ */
16
+ key: string;
17
+ }
18
+
19
+ interface BoundColumn<T extends Data> extends BaseColumn<T> {
5
20
  /** The name of the property on the Data object that should be rendered in that column. */
6
21
  propertyName: keyof T;
7
- /** The width of the column as CSS size (default: 1fr) */
8
- size?: string;
9
- /** Column's header label */
10
- label?: string;
22
+
11
23
  /**
12
24
  * A custom render function that will be called for every row(except data entry row) that should be rendered.
13
25
  * The DynamicDataList will already try to render the data in a meaningful way, but in some advanced use cases more control is required.
14
- * @memberof Column
15
26
  */
16
27
  render?: ColumnRenderer<T>;
28
+
29
+ /**
30
+ * The identifier for the column. (Default: propertyName)
31
+ */
32
+ key?: string;
33
+ }
34
+
35
+ export interface BaseColumn<T extends Data> {
36
+ /** The width of the column as CSS size (default: 1fr) */
37
+ size?: string;
38
+ /** Column's header label */
39
+ label?: string;
17
40
  /**
18
41
  * If set to false, will not render a tooltip for the column data (default: undefined)
19
42
  * If a custom renderer is used that will not return a value cast-able to a string, you should consider setting this property to false and add a title attribute on the component returned by the custom renderer.
@@ -39,6 +62,10 @@ export interface DynamicListColumn<T extends Data> {
39
62
  disableResizing?: boolean;
40
63
  }
41
64
 
65
+ export type DynamicListColumn<T extends Data> =
66
+ | UnboundColumn<T>
67
+ | BoundColumn<T>;
68
+
42
69
  export type DynamicListDataEntryRenderer = (
43
70
  /** Value to render. */
44
71
  currentValue: unknown,
@@ -119,7 +119,21 @@ export default meta;
119
119
  export const Default: StoryObj<StoryDDLType> = {
120
120
  args: {
121
121
  ...defaultProps,
122
- positionPropertyName: 'position',
122
+ },
123
+ };
124
+
125
+ export const UnboundColumn: StoryObj<StoryDDLType> = {
126
+ args: {
127
+ ...defaultProps,
128
+ columns: [
129
+ ...defaultColumns,
130
+ {
131
+ label: 'Unbound',
132
+ render: () => 'Unbound',
133
+ key: 'unbound',
134
+ size: '150px',
135
+ },
136
+ ],
123
137
  },
124
138
  };
125
139
 
@@ -158,7 +158,7 @@ export const DynamicListDataEntry = <T extends Data>({
158
158
 
159
159
  let transformedState: T = state;
160
160
  for (const { onAddTransformer, propertyName } of columns) {
161
- if (onAddTransformer) {
161
+ if (onAddTransformer && propertyName) {
162
162
  transformedState = {
163
163
  ...transformedState,
164
164
  [propertyName]: onAddTransformer(
@@ -217,9 +217,12 @@ export const DynamicListDataEntry = <T extends Data>({
217
217
  >
218
218
  {renderField<T>(
219
219
  column,
220
- state[column.propertyName],
220
+ column.propertyName && state[column.propertyName],
221
221
  error[column.propertyName as string],
222
- (value) => valueChangedHandler(column.propertyName, value),
222
+ column.propertyName
223
+ ? (value) =>
224
+ valueChangedHandler(column.propertyName as keyof T, value)
225
+ : noop,
223
226
  disabled,
224
227
  mode,
225
228
  )}
@@ -97,7 +97,7 @@ export const DynamicListHeader = <T extends Data>({
97
97
  )}
98
98
  {columns.map((column, i) => (
99
99
  <div
100
- key={column.propertyName as string}
100
+ key={column.key ?? (column.propertyName as string)}
101
101
  className={clsx(classes.column)}
102
102
  ref={cols[i].ref}
103
103
  data-test-id={`dynamic-list-header-property:${
@@ -165,11 +165,7 @@ export const DynamicListRow = <T extends Data>({
165
165
  </div>
166
166
  )}
167
167
  {columns.map((column: DynamicListColumn<T>) => {
168
- const columnData: React.ReactNode = renderData<T>(
169
- column,
170
- data,
171
- column.propertyName,
172
- );
168
+ const columnData: React.ReactNode = renderData<T>(column, data);
173
169
 
174
170
  return (
175
171
  <div
@@ -177,13 +173,13 @@ export const DynamicListRow = <T extends Data>({
177
173
  [classes.editable]:
178
174
  column.dataEntryRender !== undefined && allowEditing,
179
175
  })}
180
- key={column.propertyName as string}
176
+ key={column.key ?? (column.propertyName as string)}
181
177
  >
182
178
  <div
183
179
  className={classes.column}
184
180
  title={
185
181
  column.tooltip !== false
186
- ? getTooltipText(data[column.propertyName])
182
+ ? getTooltipText(columnData)
187
183
  : undefined
188
184
  }
189
185
  data-test-id={`dynamic-list-property:${
@@ -229,9 +225,11 @@ export const DynamicListRow = <T extends Data>({
229
225
  const renderData = function <T extends Data>(
230
226
  column: DynamicListColumn<T>,
231
227
  data: T,
232
- propertyName: keyof T,
233
228
  ): React.ReactNode {
234
- const value: unknown = data[propertyName];
229
+ if (!column.propertyName) {
230
+ return column.render!(undefined, data);
231
+ }
232
+ const value: unknown = data[column.propertyName];
235
233
  if (column.render) {
236
234
  return column.render(value, data);
237
235
  }
@@ -18,9 +18,40 @@ export interface ColumnSortKeys {
18
18
  descending: string;
19
19
  }
20
20
 
21
- export interface Column<T extends Data> {
21
+ interface UnboundColumn<T extends Data> extends BaseColumn<T> {
22
+ /** The name of the property on the Data object that should be rendered in that column. */
23
+ propertyName?: keyof T;
24
+ /**
25
+ * A custom render function that will be called for every row that should be rendered.
26
+ * The List will already try to render the data in a meaningful way, but in some advanced use cases more control is required.
27
+ * @memberof Column
28
+ */
29
+ render: ColumnRenderer<T>;
30
+
31
+ /**
32
+ * The identifier for the column.
33
+ */
34
+ key: string;
35
+ }
36
+
37
+ interface BoundColumn<T extends Data> extends BaseColumn<T> {
22
38
  /** The name of the property on the Data object that should be rendered in that column. */
23
39
  propertyName: keyof T;
40
+
41
+ /**
42
+ * A custom render function that will be called for every row that should be rendered.
43
+ * The List will already try to render the data in a meaningful way, but in some advanced use cases more control is required.
44
+ * @memberof Column
45
+ */
46
+ render?: ColumnRenderer<T>;
47
+
48
+ /**
49
+ * The identifier for the column. (Default: propertyName)
50
+ */
51
+ key?: string;
52
+ }
53
+
54
+ interface BaseColumn<T extends Data> {
24
55
  /** The width of the column as CSS size (default: 1fr) */
25
56
  size?: string;
26
57
  /** Column's header label */
@@ -51,6 +82,8 @@ export interface Column<T extends Data> {
51
82
  disableResizing?: boolean;
52
83
  }
53
84
 
85
+ export type Column<T extends Data> = UnboundColumn<T> | BoundColumn<T>;
86
+
54
87
  export interface ColumnMap {
55
88
  [key: string]: string;
56
89
  }
@@ -165,6 +165,24 @@ export const Default: StoryObj<StoryListType> = {
165
165
  }),
166
166
  };
167
167
 
168
+ export const WithUnboundColumn: StoryObj<StoryListType> = {
169
+ name: 'With unbound column',
170
+ args: {
171
+ columns: [
172
+ ...defaultColumns,
173
+ {
174
+ // Columns don't have to be bound to a property.
175
+ // They can also just define a renderer and infer the cell data from the row data.
176
+ label: 'Unbound',
177
+ render: (_, data) => <span>{data.desc.substring(15, 20)}</span>,
178
+ key: 'unbound',
179
+ size: '100px',
180
+ },
181
+ ],
182
+ },
183
+ render: Default.render,
184
+ };
185
+
168
186
  export const Navigating: StoryObj<StoryListType> = {
169
187
  name: 'Navigating using DOM links',
170
188
  args: {
@@ -155,7 +155,7 @@ const useSort = <T,>(
155
155
  */
156
156
  export const List = <T extends Data>({
157
157
  columns,
158
- data,
158
+ data = [],
159
159
  isLoading = false,
160
160
  isError = false,
161
161
  errorMsg = 'There was an error.',
@@ -4,11 +4,12 @@ import { noop } from '../../../../helpers/utils';
4
4
  import { ColumnSortKeys, SortData } from '../../List.model';
5
5
  import classes from './ColumnLabel.scss';
6
6
 
7
- export interface ColumnLabelProps<T> {
8
- /** Data object's property name associated with this column */
9
- propertyName: keyof T;
7
+ interface ColumnLabelProps<T> {
10
8
  /** Column label */
11
9
  label?: string;
10
+ /** Data object's property name associated with this column */
11
+ propertyName: keyof T | undefined;
12
+
12
13
  /** If set to false, disables sorting for this column (default: true) */
13
14
  sortable?: boolean;
14
15
  /** If set, allow to directly specify which sorting enum value to use */
@@ -20,15 +21,15 @@ export interface ColumnLabelProps<T> {
20
21
  }
21
22
 
22
23
  export const ColumnLabel = <T,>({
23
- propertyName,
24
24
  label,
25
+ propertyName,
25
26
  sortable = true,
26
27
  columnSortKeys,
27
28
  sortData,
28
29
  onSortChanged = noop,
29
30
  }: PropsWithChildren<ColumnLabelProps<T>>): JSX.Element => {
30
31
  /** Creates sort data object to be emitted back on change event. */
31
- const getSortDir = (): SortData<T> => {
32
+ const getSortDir = (propertyName: keyof T): SortData<T> => {
32
33
  if (sortData?.column === propertyName && sortData.direction === 'asc') {
33
34
  return {
34
35
  column: propertyName,
@@ -46,9 +47,9 @@ export const ColumnLabel = <T,>({
46
47
 
47
48
  /** Determines if the current column is currently being used to sort and which direction. */
48
49
  const direction =
49
- sortData?.column === propertyName ? sortData.direction : undefined;
50
+ sortData?.column === propertyName ? sortData?.direction : undefined;
50
51
 
51
- if (!sortable) {
52
+ if (!propertyName || !sortable) {
52
53
  return (
53
54
  <div
54
55
  className={classes.container}
@@ -62,7 +63,9 @@ export const ColumnLabel = <T,>({
62
63
  <div className={classes.container}>
63
64
  <div
64
65
  className={clsx(classes.clickWrapper, classes.sortable)}
65
- onClick={() => onSortChanged(getSortDir())}
66
+ onClick={
67
+ propertyName && (() => onSortChanged(getSortDir(propertyName)))
68
+ }
66
69
  data-test-id={`list-label:${String(propertyName)}`}
67
70
  data-test-sort-dir={direction ?? 'none'}
68
71
  >
@@ -95,7 +95,7 @@ export const ListHeader = <T extends Data>({
95
95
  >
96
96
  {columns.map((column, i) => (
97
97
  <div
98
- key={column.propertyName as string}
98
+ key={column.key ?? (column.propertyName as string)}
99
99
  ref={cols[i].ref}
100
100
  className={clsx(classes.columnLabel)}
101
101
  >
@@ -84,9 +84,11 @@ setLocale(navigator.language);
84
84
  function renderData<T extends Data>(
85
85
  column: Column<T>,
86
86
  rowData: T,
87
- propertyName: keyof T,
88
87
  ): React.ReactNode {
89
- const value: unknown = rowData[propertyName];
88
+ if (!column.propertyName) {
89
+ return column.render!(undefined, rowData);
90
+ }
91
+ const value: unknown = rowData[column.propertyName];
90
92
  if (column.render) {
91
93
  return column.render(value, rowData);
92
94
  }
@@ -197,14 +199,13 @@ export const ListRow = <T extends Data>({
197
199
  typeof onItemClicked !== 'function';
198
200
 
199
201
  const generateCells: JSX.Element[] = columns.map((column: Column<T>) => {
200
- const columnData: React.ReactNode = renderData<T>(
201
- column,
202
- data,
203
- column.propertyName,
204
- );
202
+ const columnData: React.ReactNode = renderData<T>(column, data);
205
203
 
206
204
  return (
207
- <div key={column.propertyName as string} className={classes.cellWrapper}>
205
+ <div
206
+ key={column.key ?? (column.propertyName as string)}
207
+ className={classes.cellWrapper}
208
+ >
208
209
  <div
209
210
  className={classes.cell}
210
211
  title={