@axinom/mosaic-ui 0.64.0-rc.7 → 0.65.0-rc.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 (39) hide show
  1. package/dist/components/Explorer/BulkEdit/FormFieldsConfigConverter.d.ts +1 -1
  2. package/dist/components/Explorer/BulkEdit/GenerateMutation.d.ts.map +1 -1
  3. package/dist/components/Explorer/BulkEdit/index.d.ts +1 -0
  4. package/dist/components/Explorer/BulkEdit/index.d.ts.map +1 -1
  5. package/dist/components/Explorer/QuickEdit/useQuickEdit.d.ts.map +1 -1
  6. package/dist/components/Explorer/index.d.ts +1 -1
  7. package/dist/components/Explorer/index.d.ts.map +1 -1
  8. package/dist/components/InfoTooltip/InfoTooltip.d.ts.map +1 -1
  9. package/dist/components/InlineMenu/InlineMenu.d.ts.map +1 -1
  10. package/dist/components/List/List.d.ts +6 -1
  11. package/dist/components/List/List.d.ts.map +1 -1
  12. package/dist/components/List/ListRow/ListRow.d.ts +5 -20
  13. package/dist/components/List/ListRow/ListRow.d.ts.map +1 -1
  14. package/dist/components/List/ListRowRenderer/ListRowRenderer.d.ts +22 -0
  15. package/dist/components/List/ListRowRenderer/ListRowRenderer.d.ts.map +1 -0
  16. package/dist/index.es.js +4 -5
  17. package/dist/index.es.js.map +1 -1
  18. package/dist/index.js +4 -5
  19. package/dist/index.js.map +1 -1
  20. package/package.json +2 -2
  21. package/src/components/Explorer/BulkEdit/FormFieldsConfigConverter.spec.tsx +158 -14
  22. package/src/components/Explorer/BulkEdit/FormFieldsConfigConverter.tsx +2 -2
  23. package/src/components/Explorer/BulkEdit/GenerateMutation.spec.tsx +209 -0
  24. package/src/components/Explorer/BulkEdit/GenerateMutation.tsx +78 -12
  25. package/src/components/Explorer/BulkEdit/index.ts +1 -0
  26. package/src/components/Explorer/QuickEdit/useQuickEdit.tsx +1 -0
  27. package/src/components/Explorer/index.ts +1 -0
  28. package/src/components/InfoTooltip/InfoTooltip.scss +65 -65
  29. package/src/components/InfoTooltip/InfoTooltip.tsx +35 -31
  30. package/src/components/InlineMenu/InlineMenu.tsx +39 -33
  31. package/src/components/List/List.spec.tsx +209 -1
  32. package/src/components/List/List.stories.tsx +76 -0
  33. package/src/components/List/List.tsx +52 -31
  34. package/src/components/List/ListRow/ListRow.scss +5 -0
  35. package/src/components/List/ListRow/ListRow.spec.tsx +97 -155
  36. package/src/components/List/ListRow/ListRow.tsx +31 -57
  37. package/src/components/List/ListRowRenderer/ListRowRenderer.spec.tsx +353 -0
  38. package/src/components/List/ListRowRenderer/ListRowRenderer.tsx +68 -0
  39. package/src/components/PageHeader/PageHeaderActionsGroup/PageHeaderActionsGroup.scss +32 -32
@@ -16,88 +16,88 @@ $pop-up-arrow-extrusion: -7px;
16
16
  stroke: var(--infotooltip-icon-color, $infotooltip-icon-color);
17
17
  }
18
18
  }
19
+ }
19
20
 
20
- .container {
21
- @include boxSizing;
21
+ .container {
22
+ @include boxSizing;
22
23
 
23
- box-sizing: border-box;
24
- min-height: 50px;
25
- max-width: 650px;
26
- display: grid;
27
- grid: min-content 1fr / 1fr;
28
- width: max-content;
24
+ box-sizing: border-box;
25
+ min-height: 50px;
26
+ max-width: 650px;
27
+ display: grid;
28
+ grid: min-content 1fr / 1fr;
29
+ width: max-content;
29
30
 
30
- background-color: var(
31
- --infotooltip-background-color,
32
- $infotooltip-background-color
33
- );
31
+ background-color: var(
32
+ --infotooltip-background-color,
33
+ $infotooltip-background-color
34
+ );
34
35
 
35
- color: var(--infotooltip-text-color, $infotooltip-text-color);
36
- padding: 15px;
36
+ color: var(--infotooltip-text-color, $infotooltip-text-color);
37
+ padding: 15px;
37
38
 
38
- z-index: 10;
39
+ z-index: 10;
39
40
 
40
- .arrow,
41
- .arrow::before {
42
- position: absolute;
43
- width: 14px;
44
- height: 14px;
45
- background: inherit;
46
- }
41
+ .arrow,
42
+ .arrow::before {
43
+ position: absolute;
44
+ width: 14px;
45
+ height: 14px;
46
+ background: inherit;
47
+ }
47
48
 
48
- .arrow {
49
- z-index: -1;
50
- visibility: hidden;
49
+ .arrow {
50
+ z-index: -1;
51
+ visibility: hidden;
51
52
 
52
- background-color: var(
53
- --infotooltip-background-color,
54
- $infotooltip-background-color
55
- );
56
- }
53
+ background-color: var(
54
+ --infotooltip-background-color,
55
+ $infotooltip-background-color
56
+ );
57
+ }
57
58
 
58
- .arrow::before {
59
- visibility: visible;
60
- content: '';
61
- transform: rotate(45deg);
62
- }
59
+ .arrow::before {
60
+ visibility: visible;
61
+ content: '';
62
+ transform: rotate(45deg);
63
+ }
63
64
 
64
- &[data-popper-placement^='top'] {
65
- > .arrow {
66
- bottom: $pop-up-arrow-extrusion;
67
- }
65
+ &[data-popper-placement^='top'] {
66
+ > .arrow {
67
+ bottom: $pop-up-arrow-extrusion;
68
68
  }
69
+ }
69
70
 
70
- &[data-popper-placement^='bottom'] {
71
- > .arrow {
72
- top: $pop-up-arrow-extrusion;
73
- }
71
+ &[data-popper-placement^='bottom'] {
72
+ > .arrow {
73
+ top: $pop-up-arrow-extrusion;
74
74
  }
75
+ }
75
76
 
76
- &[data-popper-placement^='left'] {
77
- > .arrow {
78
- right: $pop-up-arrow-extrusion;
79
- }
77
+ &[data-popper-placement^='left'] {
78
+ > .arrow {
79
+ right: $pop-up-arrow-extrusion;
80
80
  }
81
+ }
81
82
 
82
- &[data-popper-placement^='right'] {
83
- > .arrow {
84
- left: $pop-up-arrow-extrusion;
85
- }
83
+ &[data-popper-placement^='right'] {
84
+ > .arrow {
85
+ left: $pop-up-arrow-extrusion;
86
86
  }
87
87
  }
88
+ }
88
89
 
89
- .info-tooltip-enter {
90
- opacity: 0;
91
- }
92
- .info-tooltip-enter-active {
93
- opacity: 1;
94
- transition: opacity 500ms ease-in;
95
- }
96
- .info-tooltip-exit {
97
- opacity: 1;
98
- }
99
- .info-tooltip-exit-active {
100
- opacity: 0;
101
- transition: opacity 500ms ease-out;
102
- }
90
+ .info-tooltip-enter {
91
+ opacity: 0;
92
+ }
93
+ .info-tooltip-enter-active {
94
+ opacity: 1;
95
+ transition: opacity 500ms ease-in;
96
+ }
97
+ .info-tooltip-exit {
98
+ opacity: 1;
99
+ }
100
+ .info-tooltip-exit-active {
101
+ opacity: 0;
102
+ transition: opacity 500ms ease-out;
103
103
  }
@@ -1,5 +1,6 @@
1
1
  import clsx from 'clsx';
2
2
  import React, { useState } from 'react';
3
+ import { createPortal } from 'react-dom';
3
4
  import { usePopper } from 'react-popper';
4
5
  import { CSSTransition } from 'react-transition-group';
5
6
  import { IconName, Icons } from '../Icons';
@@ -52,40 +53,43 @@ export const InfoTooltip: React.FC<InfoTooltipProps> = ({
52
53
  >
53
54
  <Icons icon={IconName.Info} className={clsx(classes.icon)} />
54
55
  </div>
55
- <CSSTransition
56
- in={showTooltip}
57
- timeout={200}
58
- classNames={{
59
- enter: classes['info-tooltip-enter'],
60
- enterActive: classes['info-tooltip-enter-active'],
61
- exit: classes['info-tooltip-exit'],
62
- exitActive: classes['info-tooltip-exit-active'],
63
- }}
64
- unmountOnExit
65
- >
66
- <div
67
- className={clsx(classes.container)}
68
- ref={setPopperElement as React.LegacyRef<HTMLDivElement>}
69
- style={styles.popper}
70
- {...attributes.popper}
71
- onClick={(e) => e.stopPropagation()}
72
- onMouseEnter={() => {
73
- clearTimeout(hideTooltipTimeout);
56
+ {createPortal(
57
+ <CSSTransition
58
+ in={showTooltip}
59
+ timeout={200}
60
+ classNames={{
61
+ enter: classes['info-tooltip-enter'],
62
+ enterActive: classes['info-tooltip-enter-active'],
63
+ exit: classes['info-tooltip-exit'],
64
+ exitActive: classes['info-tooltip-exit-active'],
74
65
  }}
75
- onMouseLeave={() =>
76
- (hideTooltipTimeout = global.setTimeout(() => {
77
- setShowTooltip(false);
78
- }, tooltipVisibleTime))
79
- }
66
+ unmountOnExit
80
67
  >
81
- {children}
82
68
  <div
83
- ref={setArrowElement as React.LegacyRef<HTMLDivElement>}
84
- className={clsx(classes.arrow)}
85
- style={styles.arrow}
86
- />
87
- </div>
88
- </CSSTransition>
69
+ className={clsx(classes.container)}
70
+ ref={setPopperElement as React.LegacyRef<HTMLDivElement>}
71
+ style={styles.popper}
72
+ {...attributes.popper}
73
+ onClick={(e) => e.stopPropagation()}
74
+ onMouseEnter={() => {
75
+ clearTimeout(hideTooltipTimeout);
76
+ }}
77
+ onMouseLeave={() =>
78
+ (hideTooltipTimeout = global.setTimeout(() => {
79
+ setShowTooltip(false);
80
+ }, tooltipVisibleTime))
81
+ }
82
+ >
83
+ {children}
84
+ <div
85
+ ref={setArrowElement as React.LegacyRef<HTMLDivElement>}
86
+ className={clsx(classes.arrow)}
87
+ style={styles.arrow}
88
+ />
89
+ </div>
90
+ </CSSTransition>,
91
+ document.body,
92
+ )}
89
93
  </div>
90
94
  );
91
95
  };
@@ -1,6 +1,7 @@
1
1
  import { Placement } from '@popperjs/core';
2
2
  import clsx from 'clsx';
3
3
  import React, { Ref, useState } from 'react';
4
+ import { createPortal } from 'react-dom';
4
5
  import { usePopper } from 'react-popper';
5
6
  import { useExpand } from '../../hooks/useExpand/useExpand';
6
7
  import { Actions, ActionsProps } from '../Actions';
@@ -104,40 +105,45 @@ export const InlineMenu: React.FC<InlineMenuProps> = ({
104
105
  )}
105
106
  dataTestId="inline-menu-button"
106
107
  />
107
- {isExpanded && (
108
- <div
109
- tabIndex={-1}
110
- className={clsx(classes.container, 'sub-menu-container', className)}
111
- ref={setPopperElement as React.LegacyRef<HTMLDivElement>}
112
- style={styles.popper}
113
- {...attributes.popper}
114
- onBlur={() => {
115
- onBlurTimeout = global.setTimeout(() => collapse(), 250);
116
- }}
117
- data-test-id="inline-menu"
118
- >
119
- <Actions
120
- {...rest}
121
- onActionClick={(e, action) => {
122
- if (
123
- action.confirmationMode &&
124
- action.confirmationMode !== 'None'
125
- ) {
126
- clearTimeout(onBlurTimeout);
127
- } else {
128
- onBlurTimeout = global.setTimeout(() => collapse(), 250);
129
- }
130
- onActionClick?.(e, action);
131
- }}
132
- />
108
+ {isExpanded &&
109
+ createPortal(
133
110
  <div
134
- ref={setArrowElement as React.LegacyRef<HTMLDivElement>}
135
- className={classes.arrow}
136
- // style={styles.arrow}
137
- style={{ ...styles.arrow, display: showArrow ? 'inherit' : 'none' }}
138
- />
139
- </div>
140
- )}
111
+ tabIndex={-1}
112
+ className={clsx(classes.container, 'sub-menu-container', className)}
113
+ ref={setPopperElement as React.LegacyRef<HTMLDivElement>}
114
+ style={styles.popper}
115
+ {...attributes.popper}
116
+ onBlur={() => {
117
+ onBlurTimeout = global.setTimeout(() => collapse(), 250);
118
+ }}
119
+ data-test-id="inline-menu"
120
+ >
121
+ <Actions
122
+ {...rest}
123
+ onActionClick={(e, action) => {
124
+ if (
125
+ action.confirmationMode &&
126
+ action.confirmationMode !== 'None'
127
+ ) {
128
+ clearTimeout(onBlurTimeout);
129
+ } else {
130
+ onBlurTimeout = global.setTimeout(() => collapse(), 250);
131
+ }
132
+ onActionClick?.(e, action);
133
+ }}
134
+ />
135
+ <div
136
+ ref={setArrowElement as React.LegacyRef<HTMLDivElement>}
137
+ className={classes.arrow}
138
+ // style={styles.arrow}
139
+ style={{
140
+ ...styles.arrow,
141
+ display: showArrow ? 'inherit' : 'none',
142
+ }}
143
+ />
144
+ </div>,
145
+ document.body,
146
+ )}
141
147
  </>
142
148
  );
143
149
  };
@@ -9,6 +9,7 @@ import { Column, ListElement, ListSelectMode, SortData } from './List.model';
9
9
  import { ListHeader } from './ListHeader/ListHeader';
10
10
  import { ListRow } from './ListRow/ListRow';
11
11
  import { ListRowLoader } from './ListRow/ListRowLoader';
12
+ import { ListRowRenderer } from './ListRowRenderer/ListRowRenderer';
12
13
 
13
14
  /* eslint-disable @typescript-eslint/no-non-null-assertion */
14
15
 
@@ -475,7 +476,10 @@ describe('List', () => {
475
476
  />,
476
477
  );
477
478
 
478
- const onTriggered = wrapper.find(ListRow).first().prop('onTriggered');
479
+ const onTriggered = wrapper
480
+ .find(ListRowRenderer)
481
+ .first()
482
+ .prop('onTriggered');
479
483
  onTriggered!();
480
484
 
481
485
  expect(spy).toHaveBeenCalledTimes(1);
@@ -605,4 +609,208 @@ describe('List', () => {
605
609
 
606
610
  expect(listRow.props().actionSize).toBe(mockSize);
607
611
  });
612
+
613
+ describe('isRowDisabled', () => {
614
+ const mountListWithDisabledRows = (additionalProps = {}) => {
615
+ return mount(
616
+ <List
617
+ columns={mockListColumns}
618
+ data={mockListData}
619
+ {...additionalProps}
620
+ />,
621
+ );
622
+ };
623
+
624
+ const expectRowDisabledStates = (
625
+ wrapper: any,
626
+ expectedStates: boolean[],
627
+ ) => {
628
+ const rows = wrapper.find(ListRow);
629
+ expectedStates.forEach((expected, index) => {
630
+ expect(rows.at(index).prop('isRowDisabled')).toBe(expected);
631
+ });
632
+ };
633
+
634
+ const triggerSelectAll = (wrapper: any) => {
635
+ const headerRow = wrapper.find(ListHeader);
636
+ act(() => {
637
+ headerRow.prop('onCheckboxToggled')!(true);
638
+ });
639
+ wrapper.update();
640
+ };
641
+
642
+ it('should disable rows based on custom isRowDisabled function', () => {
643
+ const isRowDisabledMock = jest.fn(
644
+ (data: ListTestData, _index: number) => data.id === '2',
645
+ );
646
+
647
+ const wrapper = mountListWithDisabledRows({
648
+ isRowDisabled: isRowDisabledMock,
649
+ });
650
+
651
+ // Verify disabled states
652
+ expectRowDisabledStates(wrapper, [false, true, false]);
653
+
654
+ // Verify function calls
655
+ expect(isRowDisabledMock).toHaveBeenCalledTimes(3);
656
+ mockListData.forEach((data, index) => {
657
+ expect(isRowDisabledMock).toHaveBeenNthCalledWith(
658
+ index + 1,
659
+ data,
660
+ index,
661
+ );
662
+ });
663
+ });
664
+
665
+ describe('selectAll behavior', () => {
666
+ const selectAllProps = {
667
+ selectionMode: ListSelectMode.Multi,
668
+ enableSelectAll: true,
669
+ };
670
+
671
+ it('should disable rows when selectAll is enabled and enableSelectAllDeselect is false', () => {
672
+ const wrapper = mountListWithDisabledRows({
673
+ ...selectAllProps,
674
+ enableSelectAllDeselect: false,
675
+ });
676
+
677
+ // Initially all rows should be enabled
678
+ expectRowDisabledStates(wrapper, [false, false, false]);
679
+
680
+ triggerSelectAll(wrapper);
681
+
682
+ // After selectAll, all rows should be disabled
683
+ expectRowDisabledStates(wrapper, [true, true, true]);
684
+
685
+ expect(wrapper.find(ListRow)).toHaveLength(mockListData.length);
686
+ });
687
+
688
+ it('should not disable rows when enableSelectAllDeselect is true', () => {
689
+ const wrapper = mountListWithDisabledRows({
690
+ ...selectAllProps,
691
+ enableSelectAllDeselect: true,
692
+ });
693
+
694
+ triggerSelectAll(wrapper);
695
+
696
+ // Rows should remain enabled when deselect is allowed
697
+ expectRowDisabledStates(wrapper, [false, false, false]);
698
+
699
+ expect(wrapper.find(ListRow)).toHaveLength(mockListData.length);
700
+ });
701
+
702
+ it('should combine custom isRowDisabled with selectAll disabled state', () => {
703
+ const isRowDisabledMock = jest.fn(
704
+ (data: ListTestData) => data.id === '1',
705
+ );
706
+
707
+ const wrapper = mountListWithDisabledRows({
708
+ ...selectAllProps,
709
+ enableSelectAllDeselect: false,
710
+ isRowDisabled: isRowDisabledMock,
711
+ });
712
+
713
+ triggerSelectAll(wrapper);
714
+
715
+ // All rows should be disabled (selectAll OR custom logic)
716
+ expectRowDisabledStates(wrapper, [true, true, true]);
717
+
718
+ expect(wrapper.find(ListRow)).toHaveLength(mockListData.length);
719
+ });
720
+ });
721
+
722
+ // Test different scenarios with parameterized tests
723
+ describe.each([
724
+ {
725
+ name: 'even indices disabled',
726
+ disableLogic: (_data: ListTestData, index: number) => index % 2 === 0,
727
+ expected: [true, false, true],
728
+ },
729
+ {
730
+ name: 'specific IDs disabled',
731
+ disableLogic: (data: ListTestData) => ['1', '3'].includes(data.id),
732
+ expected: [true, false, true],
733
+ },
734
+ {
735
+ name: 'title-based disabled',
736
+ disableLogic: (data: ListTestData) => data.title.includes('2'),
737
+ expected: [false, true, false],
738
+ },
739
+ ])('custom logic: $name', ({ disableLogic, expected }) => {
740
+ it(`should disable rows when ${expected.filter(Boolean).length} out of ${
741
+ expected.length
742
+ } match criteria`, () => {
743
+ const isRowDisabledMock = jest.fn(disableLogic);
744
+ const wrapper = mountListWithDisabledRows({
745
+ isRowDisabled: isRowDisabledMock,
746
+ });
747
+
748
+ expectRowDisabledStates(wrapper, expected);
749
+ expect(isRowDisabledMock).toHaveBeenCalledTimes(mockListData.length);
750
+ });
751
+ });
752
+ });
753
+
754
+ describe('customRowRenderer', () => {
755
+ const createSimpleCustomRenderer = (testId = 'custom-row') =>
756
+ jest.fn(() => <div data-test-id={testId}>Custom Row Content</div>);
757
+
758
+ it('should use custom row renderer when provided', () => {
759
+ const customRowRenderer = createSimpleCustomRenderer();
760
+ const wrapper = mount(
761
+ <List
762
+ columns={mockListColumns}
763
+ data={mockListData}
764
+ customRowRenderer={customRowRenderer}
765
+ />,
766
+ );
767
+
768
+ expect(customRowRenderer).toHaveBeenCalledTimes(3);
769
+ expect(wrapper.find('[data-test-id="custom-row"]')).toHaveLength(3);
770
+ expect(wrapper.find(ListRow)).toHaveLength(0); // No default ListRow components
771
+ });
772
+
773
+ it('should fall back to default ListRow when custom renderer returns null', () => {
774
+ const customRowRenderer = jest.fn(() => null);
775
+ const wrapper = mount(
776
+ <List
777
+ columns={mockListColumns}
778
+ data={mockListData}
779
+ customRowRenderer={customRowRenderer}
780
+ />,
781
+ );
782
+
783
+ expect(customRowRenderer).toHaveBeenCalledTimes(3);
784
+ expect(wrapper.find(ListRow)).toHaveLength(3);
785
+ expect(wrapper.find('[data-test-id="custom-row"]')).toHaveLength(0);
786
+ });
787
+
788
+ it('should pass ListRowProps to custom renderer', () => {
789
+ const customRowRenderer = jest.fn(() => (
790
+ <div data-test-id="custom-row">Custom</div>
791
+ ));
792
+
793
+ mount(
794
+ <List
795
+ columns={mockListColumns}
796
+ data={mockListData}
797
+ customRowRenderer={customRowRenderer}
798
+ />,
799
+ );
800
+
801
+ // Verify first call receives ListRowProps structure
802
+ expect(customRowRenderer).toHaveBeenNthCalledWith(
803
+ 1,
804
+ expect.objectContaining({
805
+ data: mockListData[0],
806
+ columns: mockListColumns,
807
+ columnSizes: '1fr 1fr 1fr 50px',
808
+ itemSelected: false,
809
+ isRowDisabled: false,
810
+ onItemClicked: expect.any(Function),
811
+ onItemSelected: expect.any(Function),
812
+ }),
813
+ );
814
+ });
815
+ });
608
816
  });
@@ -11,6 +11,7 @@ import { Message } from '../Message';
11
11
  import { List, ListProps } from './List';
12
12
  import { Column, ColumnMap, ListSelectMode, SortData } from './List.model';
13
13
  import { sortStoryData, useLocalSort } from './List.stories.helper';
14
+ import { ListRowProps } from './ListRow/ListRow';
14
15
  import { createStateRenderer } from './ListRow/Renderers';
15
16
 
16
17
  interface ListStoryData {
@@ -88,6 +89,8 @@ const groups = createGroups({
88
89
  'enableSelectAll',
89
90
  'showActionButton',
90
91
  'loadingTriggerOffset',
92
+ 'isRowDisabled',
93
+ 'customRowRenderer',
91
94
  ],
92
95
  Styling: [
93
96
  'horizontalTextAlign',
@@ -133,6 +136,79 @@ const meta: Meta<StoryListType> = {
133
136
  random: (): boolean => (Math.random() > 0.5 ? true : false),
134
137
  },
135
138
  },
139
+ isRowDisabled: {
140
+ ...groups.isRowDisabled,
141
+ description:
142
+ 'Function to determine if a row should be disabled. Choose from predefined options or disable control for custom logic.',
143
+ control: {
144
+ type: 'select',
145
+ },
146
+ options: ['none', 'evenRows', 'oddRows', 'specificIds', 'randomRows'],
147
+ mapping: {
148
+ none: undefined,
149
+ evenRows: (_data: ListStoryData, index: number) => index % 2 === 0,
150
+ oddRows: (_data: ListStoryData, index: number) => index % 2 !== 0,
151
+ specificIds: (data: ListStoryData) => [1, 3, 5].includes(data.id),
152
+ randomRows: () => Math.random() > 0.7,
153
+ },
154
+ },
155
+ customRowRenderer: {
156
+ ...groups.customRowRenderer,
157
+ description:
158
+ 'Custom renderer for rows. Choose from predefined options or disable control for custom logic.',
159
+ control: {
160
+ type: 'select',
161
+ },
162
+ options: ['none', 'customRenderer'],
163
+ mapping: {
164
+ none: undefined,
165
+ customRenderer: (props: ListRowProps<ListStoryData>) => {
166
+ const renderCustom = Math.random() < 0.3;
167
+
168
+ return renderCustom ? (
169
+ <div
170
+ style={{
171
+ display: 'grid',
172
+ gridTemplateColumns: props.columnSizes,
173
+ columnGap: props.columnGap,
174
+ height: '50px',
175
+ placeItems: 'center left',
176
+ paddingLeft: '5px',
177
+ borderBottom: '1px solid #ddd',
178
+ backgroundColor: '#f5f5f5',
179
+ opacity: props.isRowDisabled ? 0.4 : 0.7,
180
+ cursor: props.isRowDisabled ? 'not-allowed' : 'pointer',
181
+ }}
182
+ onClick={() =>
183
+ !props.isRowDisabled &&
184
+ typeof props.onItemClicked === 'function' &&
185
+ props.onItemClicked(props.data)
186
+ }
187
+ >
188
+ <span>{props.data.id}</span>
189
+ <span>{props.data.title}</span>
190
+ <div
191
+ style={{
192
+ display: 'grid',
193
+ width: '100%',
194
+ overflow: 'hidden',
195
+ }}
196
+ >
197
+ <span
198
+ style={{
199
+ overflow: 'hidden',
200
+ textOverflow: 'ellipsis',
201
+ whiteSpace: 'nowrap',
202
+ }}
203
+ >
204
+ {props.data.desc}
205
+ </span>
206
+ </div>
207
+ </div>
208
+ ) : null;
209
+ },
210
+ },
211
+ },
136
212
  },
137
213
  args: {
138
214
  columns: defaultColumns,