From a2689b4836a3e7a349ad848c5135af34175ed6bc Mon Sep 17 00:00:00 2001 From: wangdicoder Date: Fri, 10 Apr 2026 16:14:04 +1000 Subject: [PATCH 1/5] fix: update select component --- .../src/containers/theme-studio/index.tsx | 6 + .../theme-studio/runtime-presets.ts | 17 +- .../containers/theme-studio/theme-studio.scss | 2 +- .../react/src/popup/__tests__/popup.test.tsx | 83 +++++++- packages/react/src/popup/popup.tsx | 180 +++++++++++++++--- .../src/select/__tests__/select.test.tsx | 39 ++++ packages/react/src/select/select.tsx | 41 +++- .../react/src/split-button/style/index.scss | 10 +- 8 files changed, 335 insertions(+), 43 deletions(-) diff --git a/apps/docs/src/containers/theme-studio/index.tsx b/apps/docs/src/containers/theme-studio/index.tsx index 00129dcf..bd06bf93 100644 --- a/apps/docs/src/containers/theme-studio/index.tsx +++ b/apps/docs/src/containers/theme-studio/index.tsx @@ -156,6 +156,12 @@ const ThemeStudioPage = (): React.ReactElement => { ', () => { expect(document.querySelector('.ty-select__empty')).toHaveTextContent('Nothing found'); }); + it('should preserve search text after closing without selecting', () => { + const { container } = render( + + ); + const selector = container.querySelector('.ty-select__selector') as HTMLElement; + const selectEl = container.querySelector('.ty-select') as HTMLElement; + + fireEvent.click(selector); + const searchInput = container.querySelector('.ty-select__search') as HTMLInputElement; + fireEvent.change(searchInput, { target: { value: 'xyz' } }); + expect(document.querySelector('.ty-select__empty')).toHaveTextContent('Nothing found'); + + fireEvent.click(document.body); + expect(selectEl).not.toHaveClass('ty-select_open'); + expect(container.querySelector('.ty-select__search')).toHaveValue('xyz'); + + fireEvent.click(selector); + expect(document.querySelector('.ty-select__empty')).toHaveTextContent('Nothing found'); + }); + + it('should show the selected option label when reopening searchable single select', () => { + const { container } = render( + + ); + const selector = container.querySelector('.ty-select__selector') as HTMLElement; + + fireEvent.click(selector); + fireEvent.click(getOptions()[1]); + expect(container.querySelector('.ty-select__selection-text')).toHaveTextContent('Banana'); + + fireEvent.click(selector); + expect(container.querySelector('.ty-select__search')).toHaveValue('Banana'); + }); + // Sizes it('should apply size classes', () => { const { container: smContainer } = render( diff --git a/packages/react/src/select/select.tsx b/packages/react/src/select/select.tsx index 2c4ffb12..cb9cce65 100644 --- a/packages/react/src/select/select.tsx +++ b/packages/react/src/select/select.tsx @@ -48,6 +48,7 @@ const Select = (props: SelectProps): React.ReactElement => { const ref = useRef(null); const searchInputRef = useRef(null); + const wasOpenRef = useRef(false); const dropdownRef = useCallback( (node: HTMLUListElement | null) => { if (!node || !scrollToSelected) return; @@ -168,6 +169,15 @@ const Select = (props: SelectProps): React.ReactElement => { [flatOptions, labelRender] ); + const getSearchTextForValue = useCallback( + (val: string): string => { + const opt = flatOptions.find((o) => o.value === val); + const label = opt?.label ?? val; + return typeof label === 'string' || typeof label === 'number' ? String(label) : val; + }, + [flatOptions] + ); + // Remove tag const handleRemoveTag = (val: string, e: React.MouseEvent): void => { e.stopPropagation(); @@ -294,11 +304,35 @@ const Select = (props: SelectProps): React.ReactElement => { if (!combo.isOpen || !showSearch || disabled) return; const frameId = requestAnimationFrame(() => { - searchInputRef.current?.focus(); + const input = searchInputRef.current; + input?.focus(); + if (!isMultiple && hasSomeValue && input?.value) { + input.select(); + } }); return () => cancelAnimationFrame(frameId); - }, [combo.isOpen, showSearch, disabled]); + }, [combo.isOpen, disabled, hasSomeValue, isMultiple, showSearch]); + + useEffect(() => { + const wasOpen = wasOpenRef.current; + wasOpenRef.current = combo.isOpen; + if ( + wasOpen || + !combo.isOpen || + isMultiple || + !showSearch || + !hasSomeValue || + searchValue + ) { + return; + } + + const currentValue = Array.isArray(selectVal) ? selectVal[0] : selectVal; + if (currentValue) { + setSearchValue(getSearchTextForValue(currentValue)); + } + }, [combo.isOpen, getSearchTextForValue, hasSomeValue, isMultiple, searchValue, selectVal, showSearch]); const hasValue = hasSomeValue; @@ -476,7 +510,7 @@ const Select = (props: SelectProps): React.ReactElement => { } // Single mode - if (showSearch && combo.isOpen) { + if (showSearch && (combo.isOpen || searchValue)) { return ( { } combo.closeDropdown(); - setSearchValue(''); }} content={renderOverlay()}>
diff --git a/packages/react/src/split-button/style/index.scss b/packages/react/src/split-button/style/index.scss index d732662d..185a3a34 100644 --- a/packages/react/src/split-button/style/index.scss +++ b/packages/react/src/split-button/style/index.scss @@ -1,7 +1,15 @@ @use '@tiny-design/tokens/scss/variables' as *; .#{$prefix}-split-button { - &__dropdown-btn{ + display: inline-flex; + white-space: nowrap; + vertical-align: middle; + + .#{$prefix}-btn { + float: none; + } + + &__dropdown-btn { padding: 0 8px; min-width: auto; } From 19e08b98f6e35a38182b4297c0c8c4c3c63d8379 Mon Sep 17 00:00:00 2001 From: wangdicoder Date: Fri, 10 Apr 2026 16:30:49 +1000 Subject: [PATCH 2/5] chore: update docs --- packages/react/src/flex/demo/Align.tsx | 4 ++-- packages/react/src/flex/demo/Basic.tsx | 12 ++++++++++-- packages/react/src/grid/demo/Alignment.tsx | 6 +++--- packages/react/src/grid/demo/AlignmentGrid.tsx | 2 +- packages/react/src/grid/demo/Basic.tsx | 6 +++--- packages/react/src/grid/demo/DashboardShell.tsx | 14 +++++++------- packages/react/src/grid/demo/ExplicitColumns.tsx | 2 +- packages/react/src/grid/demo/Gutter.tsx | 6 +++--- packages/react/src/grid/demo/Offset.tsx | 6 +++--- packages/react/src/grid/demo/Order.tsx | 6 +++--- packages/react/src/grid/demo/Responsive.tsx | 10 +++++----- packages/react/src/layout/demo/Basic.tsx | 10 +++++----- packages/react/src/layout/demo/Sidebar.tsx | 8 ++++---- 13 files changed, 50 insertions(+), 42 deletions(-) diff --git a/packages/react/src/flex/demo/Align.tsx b/packages/react/src/flex/demo/Align.tsx index 44b99274..87bdbba8 100644 --- a/packages/react/src/flex/demo/Align.tsx +++ b/packages/react/src/flex/demo/Align.tsx @@ -12,7 +12,7 @@ export default function AlignDemo() { width: '100%', height: 120, borderRadius: 6, - border: '1px solid #6e41bf', + border: '1px solid var(--ty-color-primary)', }; return ( @@ -29,4 +29,4 @@ export default function AlignDemo() { ); -} \ No newline at end of file +} diff --git a/packages/react/src/flex/demo/Basic.tsx b/packages/react/src/flex/demo/Basic.tsx index ba75a153..1db08357 100644 --- a/packages/react/src/flex/demo/Basic.tsx +++ b/packages/react/src/flex/demo/Basic.tsx @@ -18,9 +18,17 @@ export default function BasicDemo() { {Array.from({ length: 4 }).map((_, i) => ( -
+
))} ); -} \ No newline at end of file +} diff --git a/packages/react/src/grid/demo/Alignment.tsx b/packages/react/src/grid/demo/Alignment.tsx index efc14825..6acf5f7f 100644 --- a/packages/react/src/grid/demo/Alignment.tsx +++ b/packages/react/src/grid/demo/Alignment.tsx @@ -34,11 +34,11 @@ export default function AlignmentDemo() { }; const lighterBox = { - backgroundColor: '#6E41BFD6', + backgroundColor: 'color-mix(in srgb, var(--ty-color-primary) 84%, transparent)', }; const darkerBox = { - backgroundColor: '#6E41BFFA', + backgroundColor: 'color-mix(in srgb, var(--ty-color-primary) 98%, transparent)', }; return ( @@ -68,4 +68,4 @@ export default function AlignmentDemo() { ); -} \ No newline at end of file +} diff --git a/packages/react/src/grid/demo/AlignmentGrid.tsx b/packages/react/src/grid/demo/AlignmentGrid.tsx index 481471ac..c2ede508 100644 --- a/packages/react/src/grid/demo/AlignmentGrid.tsx +++ b/packages/react/src/grid/demo/AlignmentGrid.tsx @@ -13,7 +13,7 @@ export default function AlignmentGridDemo() { padding: 16, border: '1px dashed var(--ty-color-border)', borderRadius: 12, - background: 'linear-gradient(180deg, rgba(110,65,191,0.06), rgba(110,65,191,0.01))', + background: 'linear-gradient(180deg, color-mix(in srgb, var(--ty-color-primary) 6%, transparent), color-mix(in srgb, var(--ty-color-primary-bg) 45%, transparent))', }} > diff --git a/packages/react/src/grid/demo/Basic.tsx b/packages/react/src/grid/demo/Basic.tsx index 5c57c08d..e7d3f226 100644 --- a/packages/react/src/grid/demo/Basic.tsx +++ b/packages/react/src/grid/demo/Basic.tsx @@ -13,11 +13,11 @@ export default function BasicDemo() { }; const lighterBox = { - backgroundColor: '#6E41BFD6', + backgroundColor: 'color-mix(in srgb, var(--ty-color-primary) 84%, transparent)', }; const darkerBox = { - backgroundColor: '#6E41BFFA', + backgroundColor: 'color-mix(in srgb, var(--ty-color-primary) 98%, transparent)', }; return ( @@ -42,4 +42,4 @@ export default function BasicDemo() { ); -} \ No newline at end of file +} diff --git a/packages/react/src/grid/demo/DashboardShell.tsx b/packages/react/src/grid/demo/DashboardShell.tsx index 59bfa1a1..e8ef56ea 100644 --- a/packages/react/src/grid/demo/DashboardShell.tsx +++ b/packages/react/src/grid/demo/DashboardShell.tsx @@ -12,7 +12,7 @@ const shellCardStyle: React.CSSProperties = { const metricStyle: React.CSSProperties = { ...shellCardStyle, minHeight: 144, - background: 'linear-gradient(180deg, rgba(110,65,191,0.1), rgba(110,65,191,0.02))', + background: 'linear-gradient(180deg, color-mix(in srgb, var(--ty-color-primary) 10%, transparent), color-mix(in srgb, var(--ty-color-primary-bg) 70%, transparent))', }; const sectionLabelStyle: React.CSSProperties = { @@ -108,12 +108,12 @@ export default function DashboardShellDemo() { Last 7 days
-
-
-
-
-
-
+
+
+
+
+
+
Wide content region spanning two columns on desktop. diff --git a/packages/react/src/grid/demo/ExplicitColumns.tsx b/packages/react/src/grid/demo/ExplicitColumns.tsx index 66ae38cb..af1d27b3 100644 --- a/packages/react/src/grid/demo/ExplicitColumns.tsx +++ b/packages/react/src/grid/demo/ExplicitColumns.tsx @@ -7,7 +7,7 @@ const panelStyle: React.CSSProperties = { display: 'flex', flexDirection: 'column', justifyContent: 'space-between', - background: 'linear-gradient(180deg, rgba(110,65,191,0.12), rgba(110,65,191,0.02))', + background: 'linear-gradient(180deg, color-mix(in srgb, var(--ty-color-primary) 12%, transparent), color-mix(in srgb, var(--ty-color-primary-bg) 70%, transparent))', }; export default function ExplicitColumnsDemo() { diff --git a/packages/react/src/grid/demo/Gutter.tsx b/packages/react/src/grid/demo/Gutter.tsx index 30448d32..9243faa7 100644 --- a/packages/react/src/grid/demo/Gutter.tsx +++ b/packages/react/src/grid/demo/Gutter.tsx @@ -9,11 +9,11 @@ export default function GutterDemo() { }; const lighterBox = { - backgroundColor: '#6E41BFD6', + backgroundColor: 'color-mix(in srgb, var(--ty-color-primary) 84%, transparent)', }; const darkerBox = { - backgroundColor: '#6E41BFFA', + backgroundColor: 'color-mix(in srgb, var(--ty-color-primary) 98%, transparent)', }; const [gutter, setGutter] = React.useState(8); @@ -46,4 +46,4 @@ export default function GutterDemo() { ); -} \ No newline at end of file +} diff --git a/packages/react/src/grid/demo/Offset.tsx b/packages/react/src/grid/demo/Offset.tsx index f139681a..d3570d59 100644 --- a/packages/react/src/grid/demo/Offset.tsx +++ b/packages/react/src/grid/demo/Offset.tsx @@ -13,11 +13,11 @@ export default function OffsetDemo() { }; const lighterBox = { - backgroundColor: '#6E41BFD6', + backgroundColor: 'color-mix(in srgb, var(--ty-color-primary) 84%, transparent)', }; const darkerBox = { - backgroundColor: '#6E41BFFA', + backgroundColor: 'color-mix(in srgb, var(--ty-color-primary) 98%, transparent)', }; return ( @@ -35,4 +35,4 @@ export default function OffsetDemo() { ); -} \ No newline at end of file +} diff --git a/packages/react/src/grid/demo/Order.tsx b/packages/react/src/grid/demo/Order.tsx index 225044cb..4b5b3be3 100644 --- a/packages/react/src/grid/demo/Order.tsx +++ b/packages/react/src/grid/demo/Order.tsx @@ -9,11 +9,11 @@ export default function OrderDemo() { }; const lighterBox = { - backgroundColor: '#6E41BFD6', + backgroundColor: 'color-mix(in srgb, var(--ty-color-primary) 84%, transparent)', }; const darkerBox = { - backgroundColor: '#6E41BFFA', + backgroundColor: 'color-mix(in srgb, var(--ty-color-primary) 98%, transparent)', }; return ( @@ -26,4 +26,4 @@ export default function OrderDemo() { ); -} \ No newline at end of file +} diff --git a/packages/react/src/grid/demo/Responsive.tsx b/packages/react/src/grid/demo/Responsive.tsx index 1310d90e..ff113605 100644 --- a/packages/react/src/grid/demo/Responsive.tsx +++ b/packages/react/src/grid/demo/Responsive.tsx @@ -15,18 +15,18 @@ export default function ResponsiveDemo() { <> -
xs=24 sm=12 md=8 lg=6
+
xs=24 sm=12 md=8 lg=6
-
xs=24 sm=12 md=8 lg=6
+
xs=24 sm=12 md=8 lg=6
-
xs=24 sm=12 md=8 lg=6
+
xs=24 sm=12 md=8 lg=6
-
xs=24 sm=12 md=8 lg=6
+
xs=24 sm=12 md=8 lg=6
); -} \ No newline at end of file +} diff --git a/packages/react/src/layout/demo/Basic.tsx b/packages/react/src/layout/demo/Basic.tsx index 30a9b98e..3b6f2827 100644 --- a/packages/react/src/layout/demo/Basic.tsx +++ b/packages/react/src/layout/demo/Basic.tsx @@ -7,7 +7,7 @@ export default function BasicDemo() { }; const headerStyle = { - background: 'rgba(110, 65, 191, 0.7)', + background: 'color-mix(in srgb, var(--ty-color-primary) 70%, transparent)', color: '#fff', textAlign: 'center', height: '64px', @@ -15,13 +15,13 @@ export default function BasicDemo() { }; const footerStyle = { - background: 'rgba(110, 65, 191, 0.7)', + background: 'color-mix(in srgb, var(--ty-color-primary) 70%, transparent)', color: '#fff', textAlign: 'center', }; const contentStyle = { - background: 'rgba(110, 65, 191, 0.98)', + background: 'color-mix(in srgb, var(--ty-color-primary) 98%, transparent)', color: '#fff', minHeight: '120px', lineHeight: '120px', @@ -29,7 +29,7 @@ export default function BasicDemo() { }; const sidebarStyle = { - background: 'rgba(110, 65, 191, 0.84)', + background: 'color-mix(in srgb, var(--ty-color-primary) 84%, transparent)', color: '#fff', textAlign: 'center', paddingTop: '30px', @@ -73,4 +73,4 @@ export default function BasicDemo() {
); -} \ No newline at end of file +} diff --git a/packages/react/src/layout/demo/Sidebar.tsx b/packages/react/src/layout/demo/Sidebar.tsx index 79369c19..ead51fbd 100644 --- a/packages/react/src/layout/demo/Sidebar.tsx +++ b/packages/react/src/layout/demo/Sidebar.tsx @@ -6,13 +6,13 @@ export default function SidebarDemo() { const [collapsed, setCollapsed] = React.useState(false); const sidebarStyle = { - background: 'rgba(110, 65, 191, 0.84)', + background: 'color-mix(in srgb, var(--ty-color-primary) 84%, transparent)', color: '#fff', textAlign: 'center', }; const headerStyle = { - background: 'rgba(110, 65, 191, 0.7)', + background: 'color-mix(in srgb, var(--ty-color-primary) 70%, transparent)', color: '#fff', height: '64px', lineHeight: '64px', @@ -20,7 +20,7 @@ export default function SidebarDemo() { }; const contentStyle = { - background: 'rgba(110, 65, 191, 0.98)', + background: 'color-mix(in srgb, var(--ty-color-primary) 98%, transparent)', color: '#fff', minHeight: '200px', lineHeight: '200px', @@ -47,4 +47,4 @@ export default function SidebarDemo() { ); -} \ No newline at end of file +} From f37f304ed7742eee4eedfba1d45e8a179b80c896 Mon Sep 17 00:00:00 2001 From: wangdicoder Date: Fri, 10 Apr 2026 16:42:59 +1000 Subject: [PATCH 3/5] chore: update docs --- packages/react/src/pop-confirm/demo/Basic.tsx | 6 +++--- packages/react/src/pop-confirm/demo/Icon.tsx | 6 +++--- packages/react/src/pop-confirm/demo/Locale.tsx | 6 +++--- packages/react/src/pop-confirm/index.md | 2 +- packages/react/src/pop-confirm/index.zh_CN.md | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/react/src/pop-confirm/demo/Basic.tsx b/packages/react/src/pop-confirm/demo/Basic.tsx index e177f1ce..e4c2d3a0 100644 --- a/packages/react/src/pop-confirm/demo/Basic.tsx +++ b/packages/react/src/pop-confirm/demo/Basic.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { PopConfirm, Message } from '@tiny-design/react'; +import { PopConfirm, Message, Button } from '@tiny-design/react'; export default function BasicDemo() { return ( @@ -7,9 +7,9 @@ export default function BasicDemo() { title="Are you sure to delete this?" onConfirm={() => Message.info('You clicked Yes')} > - + ); } \ No newline at end of file diff --git a/packages/react/src/pop-confirm/demo/Icon.tsx b/packages/react/src/pop-confirm/demo/Icon.tsx index 95c1e7b4..3fedd45a 100644 --- a/packages/react/src/pop-confirm/demo/Icon.tsx +++ b/packages/react/src/pop-confirm/demo/Icon.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { PopConfirm, Message } from '@tiny-design/react'; +import { PopConfirm, Message, Button } from '@tiny-design/react'; import { IconQuestionFill } from '@tiny-design/icons'; export default function IconDemo() { @@ -9,9 +9,9 @@ export default function IconDemo() { icon={} onConfirm={() => Message.info('You clicked Yes')} > - + ); } \ No newline at end of file diff --git a/packages/react/src/pop-confirm/demo/Locale.tsx b/packages/react/src/pop-confirm/demo/Locale.tsx index b1c59e05..7490c409 100644 --- a/packages/react/src/pop-confirm/demo/Locale.tsx +++ b/packages/react/src/pop-confirm/demo/Locale.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { PopConfirm, Message } from '@tiny-design/react'; +import { PopConfirm, Message, Button } from '@tiny-design/react'; export default function LocaleDemo() { return ( @@ -9,9 +9,9 @@ export default function LocaleDemo() { cancelText="Cancel" onConfirm={() => Message.info('You clicked Yes')} > - + ); } \ No newline at end of file diff --git a/packages/react/src/pop-confirm/index.md b/packages/react/src/pop-confirm/index.md index d4568a74..071bf5f0 100755 --- a/packages/react/src/pop-confirm/index.md +++ b/packages/react/src/pop-confirm/index.md @@ -70,7 +70,7 @@ Set `icon` props to customize the icon. ## Props -Inherits all [Popover](#/components/popover) props, plus: +Inherits all [Popover](../components/popover) props, plus: | Property | Description | Type | Default | | ----------- | ---------------------------- | ------------------------ | --------- | diff --git a/packages/react/src/pop-confirm/index.zh_CN.md b/packages/react/src/pop-confirm/index.zh_CN.md index 239ef0f3..1b3b61d0 100644 --- a/packages/react/src/pop-confirm/index.zh_CN.md +++ b/packages/react/src/pop-confirm/index.zh_CN.md @@ -70,7 +70,7 @@ import { PopConfirm } from 'tiny-design'; ## Props -继承所有 [Popover](#/components/popover) 属性,以及: +继承所有 [Popover](../components/popover) 属性,以及: | 属性 | 说明 | 类型 | 默认值 | | ----------- | ------------------------ | ------------------------ | --------- | From ddb395f9a8c8e4e1b1e06a528fc397ca5c3a8443 Mon Sep 17 00:00:00 2001 From: wangdicoder Date: Fri, 10 Apr 2026 16:49:38 +1000 Subject: [PATCH 4/5] chore: docs --- apps/docs/src/containers/theme-studio/preview-components.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/docs/src/containers/theme-studio/preview-components.tsx b/apps/docs/src/containers/theme-studio/preview-components.tsx index d497f969..1dffbe93 100644 --- a/apps/docs/src/containers/theme-studio/preview-components.tsx +++ b/apps/docs/src/containers/theme-studio/preview-components.tsx @@ -130,6 +130,9 @@ const subscriptionsChartConfig: ChartConfig = { }, }; +const currYear = new Date().getFullYear() +const currMonth = new Date().getMonth() + function MetricsStrip({ items = [ ['Total Revenue', '$15,231.89', '+20.1% from last month'], @@ -386,7 +389,7 @@ function CardsPreview(): React.ReactElement {
- + From 75761a5a2bb3f4ad7561c9dd550810c00b98403f Mon Sep 17 00:00:00 2001 From: wangdicoder Date: Fri, 10 Apr 2026 16:51:13 +1000 Subject: [PATCH 5/5] chore: add changeset for react component fixes --- .changeset/fix-react-component-demos.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fix-react-component-demos.md diff --git a/.changeset/fix-react-component-demos.md b/.changeset/fix-react-component-demos.md new file mode 100644 index 00000000..a76c8d63 --- /dev/null +++ b/.changeset/fix-react-component-demos.md @@ -0,0 +1,5 @@ +--- +"@tiny-design/react": patch +--- + +Fix React component behavior and update demos to follow theme tokens.