Skip to content

Commit 6ff73db

Browse files
authored
fix(react): table selection bugs, list semantics, loader redesign, button fix (#86)
* fix(table): use Checkbox/Radio components, fix selection bugs, improve types - Replace native <input> with Checkbox and Radio components for consistent styling - Fix pagination index bug where page-relative indices caused key collisions - Fix handleSelectRow filtering against unsorted dataSource - Add columns to defaultSortOrder effect dependency array - Use paginationConfig for proper type narrowing in handlePageChange - Constrain ColumnType.dataIndex to keyof T & string - Change RowSelection from type alias to interface - Remove unused fixed prop from ColumnType - Document missing props (rowClassName, onRow, defaultSortOrder, className) * chore: add changeset for table selection and type fixes * fix(react): improve List semantics, redesign Loader spinner, fix button icon shrink List: - Use semantic <ul>/<li> elements instead of <div> for list items - Add forwardRef to ListItemMeta - Extend ListItemMetaProps with div intrinsic props - Change ListItemProps to extend <li> intrinsic elements - Fix renderItem index to use global index when paginated - Add aria-busy, role="status", and aria-live attributes - Preserve generic type T through List export Loader: - Replace dot-based indicator with CSS-only spinning border animation - Remove 4 dot spans, use single div with ::before/::after pseudo-elements - Use CSS custom property --ty-loader-border-width for size variants - Simplify SCSS by removing size-specific nested selectors Button: - Add flex-shrink: 0 to icon container to prevent shrinking - Shorten loading demo button labels useVirtualScroll: - Add enabled option to skip scroll tracking when not in virtual mode * chore: update changeset to include all component changes * fix(list): omit conflicting title from ListItemMetaProps div extension
1 parent faf68d3 commit 6ff73db

File tree

18 files changed

+176
-173
lines changed

18 files changed

+176
-173
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"@tiny-design/react": minor
3+
---
4+
5+
- Table: use Checkbox/Radio components, fix selection bugs with pagination and sorting, improve type definitions
6+
- List: use semantic `<ul>`/`<li>` elements, add forwardRef to ListItemMeta, fix paginated renderItem index, add ARIA attributes
7+
- Loader: redesign spinner with CSS-only border animation, remove dot elements
8+
- Button: fix icon container shrinking in flex layout
9+
- useVirtualScroll: add `enabled` option

packages/react/src/_utils/use-virtual-scroll.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export interface UseVirtualScrollOptions {
55
itemHeight: number;
66
containerHeight: number;
77
overscan?: number;
8+
enabled?: boolean;
89
}
910

1011
export interface UseVirtualScrollResult {
@@ -15,12 +16,14 @@ export interface UseVirtualScrollResult {
1516
}
1617

1718
export function useVirtualScroll(options: UseVirtualScrollOptions): UseVirtualScrollResult {
18-
const { itemCount, itemHeight, containerHeight, overscan = 3 } = options;
19+
const { itemCount, itemHeight, containerHeight, overscan = 3, enabled = true } = options;
1920
const [scrollTop, setScrollTop] = useState(0);
2021

2122
const onScroll = useCallback((e: React.UIEvent<HTMLElement>) => {
22-
setScrollTop(e.currentTarget.scrollTop);
23-
}, []);
23+
if (enabled) {
24+
setScrollTop(e.currentTarget.scrollTop);
25+
}
26+
}, [enabled]);
2427

2528
const totalHeight = itemCount * itemHeight;
2629

packages/react/src/button/demo/Loading.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { Button, Flex } from '@tiny-design/react';
44
export default function LoadingDemo() {
55
return (
66
<Flex gap="sm">
7-
<Button loading>Default Button</Button>
8-
<Button loading btnType="primary">Primary Button</Button>
7+
<Button loading>Default</Button>
8+
<Button loading btnType="primary">Primary</Button>
99
<Button loading btnType="link">Link</Button>
1010
</Flex>
1111
);

packages/react/src/button/style/_index.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ $btn-prefix: #{$prefix}-btn;
2929

3030
&__icon-container, &__loader {
3131
display: inline-block;
32+
flex-shrink: 0;
3233
pointer-events: none;
3334
line-height: $btn-line-height;
3435
vertical-align: middle;

packages/react/src/list/__tests__/__snapshots__/list.test.tsx.snap

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,50 +3,55 @@
33
exports[`<List /> should match the snapshot 1`] = `
44
<DocumentFragment>
55
<div
6+
aria-busy="false"
67
class="ty-list ty-list_md ty-list_split"
78
>
89
<div
910
class="ty-list__body"
1011
>
11-
<div
12-
class="ty-list-item"
12+
<ul
13+
class="ty-list__items"
1314
>
14-
<div
15-
class="ty-list-item__main"
15+
<li
16+
class="ty-list-item"
1617
>
1718
<div
18-
class="ty-list-item__content"
19+
class="ty-list-item__main"
1920
>
20-
Item 1
21+
<div
22+
class="ty-list-item__content"
23+
>
24+
Item 1
25+
</div>
2126
</div>
22-
</div>
23-
</div>
24-
<div
25-
class="ty-list-item"
26-
>
27-
<div
28-
class="ty-list-item__main"
27+
</li>
28+
<li
29+
class="ty-list-item"
2930
>
3031
<div
31-
class="ty-list-item__content"
32+
class="ty-list-item__main"
3233
>
33-
Item 2
34+
<div
35+
class="ty-list-item__content"
36+
>
37+
Item 2
38+
</div>
3439
</div>
35-
</div>
36-
</div>
37-
<div
38-
class="ty-list-item"
39-
>
40-
<div
41-
class="ty-list-item__main"
40+
</li>
41+
<li
42+
class="ty-list-item"
4243
>
4344
<div
44-
class="ty-list-item__content"
45+
class="ty-list-item__main"
4546
>
46-
Item 3
47+
<div
48+
class="ty-list-item__content"
49+
>
50+
Item 3
51+
</div>
4752
</div>
48-
</div>
49-
</div>
53+
</li>
54+
</ul>
5055
</div>
5156
</div>
5257
</DocumentFragment>

packages/react/src/list/index.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
import React from 'react';
12
import List from './list';
23
import ListItem from './list-item';
34
import ListItemMeta from './list-item-meta';
5+
import { ListProps } from './types';
46

5-
type IList = typeof List & {
7+
type IList = (<T = any>(
8+
props: ListProps<T> & React.RefAttributes<HTMLDivElement>
9+
) => React.ReactElement | null) & {
610
Item: typeof ListItem;
711
ItemMeta: typeof ListItemMeta;
12+
displayName?: string;
813
};
914

1015
const DefaultList = List as IList;

packages/react/src/list/list-item-meta.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,30 @@ import { ConfigContext } from '../config-provider/config-context';
44
import { getPrefixCls } from '../_utils/general';
55
import { ListItemMetaProps } from './types';
66

7-
const ListItemMeta = (props: ListItemMetaProps): React.ReactElement => {
8-
const { avatar, title, description, prefixCls: customisedCls, className, style } = props;
7+
const ListItemMeta = React.forwardRef<HTMLDivElement, ListItemMetaProps>((props, ref) => {
8+
const {
9+
avatar,
10+
title,
11+
description,
12+
prefixCls: customisedCls,
13+
className,
14+
style,
15+
...otherProps
16+
} = props;
917
const configContext = useContext(ConfigContext);
1018
const prefixCls = getPrefixCls('list-item-meta', configContext.prefixCls, customisedCls);
1119
const cls = classNames(prefixCls, className);
1220

1321
return (
14-
<div className={cls} style={style}>
22+
<div {...otherProps} ref={ref} className={cls} style={style}>
1523
{avatar && <div className={`${prefixCls}__avatar`}>{avatar}</div>}
1624
<div className={`${prefixCls}__content`}>
1725
{title && <div className={`${prefixCls}__title`}>{title}</div>}
1826
{description && <div className={`${prefixCls}__description`}>{description}</div>}
1927
</div>
2028
</div>
2129
);
22-
};
30+
});
2331

2432
ListItemMeta.displayName = 'ListItemMeta';
2533
export default ListItemMeta;

packages/react/src/list/list-item.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ConfigContext } from '../config-provider/config-context';
44
import { getPrefixCls } from '../_utils/general';
55
import { ListItemProps } from './types';
66

7-
const ListItem = React.forwardRef<HTMLDivElement, ListItemProps>((props, ref) => {
7+
const ListItem = React.forwardRef<HTMLLIElement, ListItemProps>((props, ref) => {
88
const {
99
extra,
1010
actions,
@@ -20,11 +20,11 @@ const ListItem = React.forwardRef<HTMLDivElement, ListItemProps>((props, ref) =>
2020
const cls = classNames(prefixCls, className);
2121

2222
return (
23-
<div {...otherProps} ref={ref} className={cls} style={style}>
23+
<li {...otherProps} ref={ref} className={cls} style={style}>
2424
<div className={`${prefixCls}__main`}>
2525
<div className={`${prefixCls}__content`}>{children}</div>
2626
{actions && actions.length > 0 && (
27-
<ul className={`${prefixCls}__actions`}>
27+
<ul className={`${prefixCls}__actions`} aria-label="Actions">
2828
{actions.map((action, i) => (
2929
<li key={i} className={`${prefixCls}__action`}>
3030
{action}
@@ -34,7 +34,7 @@ const ListItem = React.forwardRef<HTMLDivElement, ListItemProps>((props, ref) =>
3434
)}
3535
</div>
3636
{extra && <div className={`${prefixCls}__extra`}>{extra}</div>}
37-
</div>
37+
</li>
3838
);
3939
});
4040

packages/react/src/list/list.tsx

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const List = React.forwardRef<HTMLDivElement, ListProps>((props, ref) => {
5252
itemCount: dataSource.length,
5353
itemHeight,
5454
containerHeight: height ?? 0,
55+
enabled: isVirtual,
5556
});
5657

5758
const cls = classNames(prefixCls, className, {
@@ -78,7 +79,7 @@ const List = React.forwardRef<HTMLDivElement, ListProps>((props, ref) => {
7879
if (isVirtual) {
7980
if (dataSource.length === 0) {
8081
return (
81-
<div className={`${prefixCls}__empty`}>
82+
<div className={`${prefixCls}__empty`} role="status">
8283
{locale?.emptyText ?? 'No Data'}
8384
</div>
8485
);
@@ -89,10 +90,16 @@ const List = React.forwardRef<HTMLDivElement, ListProps>((props, ref) => {
8990
<React.Fragment key={start + i}>{renderItem(item, start + i)}</React.Fragment>
9091
));
9192
return (
92-
<div style={{ height: totalHeight, position: 'relative' }}>
93-
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, transform: `translateY(${offsetY}px)` }}>
93+
<div
94+
style={{ height: totalHeight, position: 'relative' }}
95+
aria-rowcount={dataSource.length}
96+
>
97+
<ul
98+
className={`${prefixCls}__items`}
99+
style={{ position: 'absolute', top: 0, left: 0, right: 0, transform: `translateY(${offsetY}px)` }}
100+
>
94101
{visibleItems}
95-
</div>
102+
</ul>
96103
</div>
97104
);
98105
}
@@ -102,18 +109,22 @@ const List = React.forwardRef<HTMLDivElement, ListProps>((props, ref) => {
102109
const items = paginatedData();
103110
if (items.length === 0 && !children) {
104111
return (
105-
<div className={`${prefixCls}__empty`}>
112+
<div className={`${prefixCls}__empty`} role="status">
106113
{locale?.emptyText ?? 'No Data'}
107114
</div>
108115
);
109116
}
110117
if (renderItem) {
118+
const page = pagination ? (pagination.current ?? currentPage) : 1;
119+
const startIndex = pagination ? (page - 1) * pageSize : 0;
111120
const rendered = items.map((item, index) => (
112-
<React.Fragment key={index}>{renderItem(item, index)}</React.Fragment>
121+
<React.Fragment key={startIndex + index}>
122+
{renderItem(item, startIndex + index)}
123+
</React.Fragment>
113124
));
114125
if (grid) {
115126
return (
116-
<div
127+
<ul
117128
className={`${prefixCls}__grid`}
118129
style={{
119130
display: 'grid',
@@ -122,10 +133,10 @@ const List = React.forwardRef<HTMLDivElement, ListProps>((props, ref) => {
122133
}}
123134
>
124135
{rendered}
125-
</div>
136+
</ul>
126137
);
127138
}
128-
return rendered;
139+
return <ul className={`${prefixCls}__items`}>{rendered}</ul>;
129140
}
130141
return children;
131142
};
@@ -144,11 +155,11 @@ const List = React.forwardRef<HTMLDivElement, ListProps>((props, ref) => {
144155
: undefined;
145156

146157
return (
147-
<div {...otherProps} ref={ref} className={cls} style={style}>
158+
<div {...otherProps} ref={ref} aria-busy={loading} className={cls} style={style}>
148159
{header && <div className={`${prefixCls}__header`}>{header}</div>}
149160
<div className={bodyCls} style={bodyStyle} onScroll={isVirtual ? onScroll : undefined}>
150161
{loading ? (
151-
<div className={`${prefixCls}__loading`}>Loading...</div>
162+
<div className={`${prefixCls}__loading`} role="status" aria-live="polite">Loading...</div>
152163
) : (
153164
renderItems()
154165
)}
@@ -171,4 +182,6 @@ const List = React.forwardRef<HTMLDivElement, ListProps>((props, ref) => {
171182
});
172183

173184
List.displayName = 'List';
174-
export default List;
185+
export default List as <T = any>(
186+
props: ListProps<T> & React.RefAttributes<HTMLDivElement>
187+
) => React.ReactElement | null;

packages/react/src/list/style/_index.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@
4949
}
5050
}
5151

52+
&__items,
53+
&__grid {
54+
list-style: none;
55+
margin: 0;
56+
padding: 0;
57+
}
58+
5259
&__empty {
5360
padding: 24px;
5461
text-align: center;

0 commit comments

Comments
 (0)