@astryxdesign/core 0.7.0-canary.fe57f54 → 0.7.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.
- package/CHANGELOG.md +186 -0
- package/dist/BottomSheet/BottomSheet.d.ts +5 -4
- package/dist/BottomSheet/BottomSheet.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.d.ts +1 -1
- package/dist/BottomSheet/BottomSheetPanel.d.ts.map +1 -1
- package/dist/BottomSheet/BottomSheetPanel.js +1 -1
- package/dist/Calendar/styles.d.ts +6 -6
- package/dist/Chat/ChatComposerInput.d.ts +0 -11
- package/dist/Chat/ChatComposerInput.d.ts.map +1 -1
- package/dist/Chat/ChatComposerInput.js +0 -11
- package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
- package/dist/Chat/useTriggerMenu.js +1 -1
- package/dist/CommandPalette/CommandPaletteList.d.ts +3 -2
- package/dist/CommandPalette/CommandPaletteList.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteList.js +3 -2
- package/dist/DateInput/TouchDateField.d.ts.map +1 -1
- package/dist/DateInput/TouchDateField.js +4 -1
- package/dist/DateTimeInput/TouchDateTimeField.d.ts.map +1 -1
- package/dist/DateTimeInput/TouchDateTimeField.js +29 -13
- package/dist/DropdownMenu/DropdownMenu.d.ts +11 -0
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +173 -11
- package/dist/DropdownMenu/DropdownMenuSubMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuSubMenu.js +13 -2
- package/dist/DropdownMenu/menuRegionsContext.d.ts +2 -0
- package/dist/DropdownMenu/menuRegionsContext.d.ts.map +1 -0
- package/dist/DropdownMenu/menuRegionsContext.js +17 -0
- package/dist/Field/inputStyles.stylex.d.ts +3 -3
- package/dist/Layout/container.stylex.d.ts +18 -20
- package/dist/Layout/container.stylex.d.ts.map +1 -1
- package/dist/Layout/container.stylex.js +15 -25
- package/dist/Layout/padding.stylex.d.ts +231 -231
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +15 -6
- package/dist/Markdown/plugins/protocol.d.ts.map +1 -1
- package/dist/Markdown/plugins/protocol.js +33 -8
- package/dist/NavItem/navItemStyles.stylex.d.ts +5 -5
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +21 -1
- package/dist/PowerSearch/PowerSearchEditPopover.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchEditPopover.js +30 -15
- package/dist/Stack/stack.stylex.d.ts +22 -22
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +89 -14
- package/dist/Table/table.stylex.d.ts +6 -6
- package/dist/TimeInput/TouchTimeField.d.ts.map +1 -1
- package/dist/TimeInput/TouchTimeField.js +4 -1
- package/dist/Tokenizer/Tokenizer.d.ts +1 -12
- package/dist/Tokenizer/Tokenizer.d.ts.map +1 -1
- package/dist/Tokenizer/Tokenizer.js +1 -13
- package/dist/Typeahead/BaseTypeahead.d.ts +0 -11
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +1 -9
- package/dist/Typeahead/Typeahead.d.ts +1 -12
- package/dist/Typeahead/Typeahead.d.ts.map +1 -1
- package/dist/Typeahead/Typeahead.js +1 -13
- package/dist/astryx.css +17 -12
- package/dist/hooks/useFocusTrap.d.ts +8 -0
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +5 -1
- package/dist/hooks/useRenamedProp.d.ts +6 -6
- package/dist/hooks/useRenamedProp.js +6 -6
- package/dist/reset.a11y.chromium.spec.d.ts +2 -0
- package/dist/reset.a11y.chromium.spec.d.ts.map +1 -0
- package/dist/reset.a11y.chromium.spec.js +176 -0
- package/dist/theme/tokens.stylex.d.ts +30 -30
- package/dist/theme/tokens.stylex.d.ts.map +1 -1
- package/dist/theme/tokens.stylex.js +18 -15
- package/dist/theme/useTheme.d.ts +1 -1
- package/package.json +4 -4
- package/src/BottomSheet/BottomSheet.doc.mjs +4 -4
- package/src/BottomSheet/BottomSheet.tsx +5 -4
- package/src/BottomSheet/BottomSheetPanel.test.tsx +6 -9
- package/src/BottomSheet/BottomSheetPanel.tsx +2 -3
- package/src/Chat/ChatComposerInput.doc.mjs +3 -3
- package/src/Chat/ChatComposerInput.test.tsx +7 -55
- package/src/Chat/ChatComposerInput.tsx +0 -36
- package/src/Chat/useTriggerMenu.tsx +1 -4
- package/src/CommandPalette/CommandPalette.spec.md +2 -0
- package/src/CommandPalette/CommandPaletteList.spec.md +189 -0
- package/src/CommandPalette/CommandPaletteList.test.tsx +47 -0
- package/src/CommandPalette/CommandPaletteList.tsx +3 -2
- package/src/DateInput/TouchDateField.tsx +3 -1
- package/src/DateTimeInput/DateTimeInputTouch.test.tsx +60 -1
- package/src/DateTimeInput/TouchDateTimeField.tsx +31 -14
- package/src/DropdownMenu/DropdownMenu.doc.mjs +12 -0
- package/src/DropdownMenu/DropdownMenu.spec.md +65 -40
- package/src/DropdownMenu/DropdownMenu.test.tsx +457 -0
- package/src/DropdownMenu/DropdownMenu.tsx +278 -49
- package/src/DropdownMenu/DropdownMenuCheckboxItem.doc.mjs +6 -3
- package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +2 -1
- package/src/DropdownMenu/DropdownMenuSubMenu.tsx +11 -1
- package/src/DropdownMenu/__tests__/MenuHeaderFooter.a11y.chromium.spec.ts +195 -0
- package/src/DropdownMenu/menuRegionsContext.ts +17 -0
- package/src/Layout/container.stylex.ts +11 -21
- package/src/Lightbox/Lightbox.test.tsx +58 -0
- package/src/Lightbox/Lightbox.tsx +12 -6
- package/src/Markdown/plugins/protocol.ts +45 -12
- package/src/Markdown/plugins/sourceLines.test.tsx +121 -0
- package/src/PowerSearch/PowerSearch.tsx +28 -1
- package/src/PowerSearch/PowerSearchEditPopover.test.tsx +284 -0
- package/src/PowerSearch/PowerSearchEditPopover.tsx +41 -13
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +33 -9
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +81 -18
- package/src/TimeInput/TouchTimeField.tsx +3 -1
- package/src/Tokenizer/Tokenizer.doc.mjs +0 -16
- package/src/Tokenizer/Tokenizer.test.tsx +12 -63
- package/src/Tokenizer/Tokenizer.tsx +1 -25
- package/src/Toolbar/Toolbar.test.tsx +29 -0
- package/src/Typeahead/BaseTypeahead.doc.mjs +0 -16
- package/src/Typeahead/BaseTypeahead.test.tsx +1 -1
- package/src/Typeahead/BaseTypeahead.tsx +1 -21
- package/src/Typeahead/Typeahead.doc.mjs +0 -7
- package/src/Typeahead/Typeahead.test.tsx +24 -42
- package/src/Typeahead/Typeahead.tsx +1 -25
- package/src/hooks/useFocusTrap.ts +5 -1
- package/src/hooks/useRenamedProp.ts +6 -6
- package/src/reset.a11y.chromium.spec.ts +178 -0
- package/src/reset.css +8 -8
- package/src/reset.test.ts +112 -0
- package/src/theme/__tests__/SpacingRootFontSize.a11y.chromium.spec.ts +198 -0
- package/src/theme/tokens.stylex.ts +18 -15
- package/src/theme/tokens.test.ts +1 -1
- package/src/theme/useTheme.test.tsx +4 -4
- package/src/theme/useTheme.ts +1 -1
|
@@ -1762,9 +1762,9 @@ describe('Tokenizer end-lane reserve', () => {
|
|
|
1762
1762
|
});
|
|
1763
1763
|
|
|
1764
1764
|
// `emptySearchResultsText` was renamed to `emptySearchText` and widened from
|
|
1765
|
-
// `string` to `ReactNode` (`spec:AST-056` FR1).
|
|
1766
|
-
//
|
|
1767
|
-
//
|
|
1765
|
+
// `string` to `ReactNode` (`spec:AST-056` FR1). The overlap ended in a minor
|
|
1766
|
+
// release (`CLN-0001`–`CLN-0004`, `spec:AST-017` FR31): the old name is no
|
|
1767
|
+
// longer part of the type and is no longer read.
|
|
1768
1768
|
describe('Tokenizer emptySearchText', () => {
|
|
1769
1769
|
async function searchForNothing() {
|
|
1770
1770
|
const input = screen.getByRole('combobox');
|
|
@@ -1847,33 +1847,6 @@ describe('Tokenizer emptySearchText', () => {
|
|
|
1847
1847
|
).not.toContain('→');
|
|
1848
1848
|
});
|
|
1849
1849
|
|
|
1850
|
-
it('treats an explicit null as not given, falling through to the old name', async () => {
|
|
1851
|
-
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
1852
|
-
render(
|
|
1853
|
-
<Tokenizer
|
|
1854
|
-
label="People"
|
|
1855
|
-
searchSource={userSource}
|
|
1856
|
-
value={[]}
|
|
1857
|
-
onChange={() => {}}
|
|
1858
|
-
debounceMs={0}
|
|
1859
|
-
emptySearchResultsText="Nobody found"
|
|
1860
|
-
emptySearchText={null}
|
|
1861
|
-
/>,
|
|
1862
|
-
);
|
|
1863
|
-
await searchForNothing();
|
|
1864
|
-
|
|
1865
|
-
// `null` is what this prop's own `??` default already treats as absent,
|
|
1866
|
-
// so the released name still supplies the message and the warning says
|
|
1867
|
-
// only that it is deprecated.
|
|
1868
|
-
await waitFor(() => {
|
|
1869
|
-
expect(screen.getByText('Nobody found')).toBeInTheDocument();
|
|
1870
|
-
});
|
|
1871
|
-
expect(warn).not.toHaveBeenCalledWith(
|
|
1872
|
-
expect.stringContaining('are both set'),
|
|
1873
|
-
);
|
|
1874
|
-
warn.mockRestore();
|
|
1875
|
-
});
|
|
1876
|
-
|
|
1877
1850
|
it('falls through to the default when null is the only value given', async () => {
|
|
1878
1851
|
render(
|
|
1879
1852
|
<Tokenizer
|
|
@@ -1892,8 +1865,10 @@ describe('Tokenizer emptySearchText', () => {
|
|
|
1892
1865
|
});
|
|
1893
1866
|
});
|
|
1894
1867
|
|
|
1895
|
-
it('
|
|
1868
|
+
it('no longer reads the removed emptySearchResultsText (CLN-0001)', async () => {
|
|
1896
1869
|
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
1870
|
+
// The unknown prop reaches the root element, which React reports.
|
|
1871
|
+
const error = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
1897
1872
|
render(
|
|
1898
1873
|
<Tokenizer
|
|
1899
1874
|
label="People"
|
|
@@ -1901,47 +1876,21 @@ describe('Tokenizer emptySearchText', () => {
|
|
|
1901
1876
|
value={[]}
|
|
1902
1877
|
onChange={() => {}}
|
|
1903
1878
|
debounceMs={0}
|
|
1879
|
+
// @ts-expect-error -- removed in the minor release (CLN-0001)
|
|
1904
1880
|
emptySearchResultsText="Nobody found"
|
|
1905
1881
|
/>,
|
|
1906
1882
|
);
|
|
1907
1883
|
await searchForNothing();
|
|
1908
1884
|
|
|
1909
1885
|
await waitFor(() => {
|
|
1910
|
-
expect(screen.getByText('
|
|
1911
|
-
});
|
|
1912
|
-
expect(warn).toHaveBeenCalledWith(
|
|
1913
|
-
expect.stringContaining(
|
|
1914
|
-
'Tokenizer: `emptySearchResultsText` is deprecated; use `emptySearchText`',
|
|
1915
|
-
),
|
|
1916
|
-
);
|
|
1917
|
-
warn.mockRestore();
|
|
1918
|
-
});
|
|
1919
|
-
|
|
1920
|
-
it('lets the new name win when both are set, and warns', async () => {
|
|
1921
|
-
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
1922
|
-
render(
|
|
1923
|
-
<Tokenizer
|
|
1924
|
-
label="People"
|
|
1925
|
-
searchSource={userSource}
|
|
1926
|
-
value={[]}
|
|
1927
|
-
onChange={() => {}}
|
|
1928
|
-
debounceMs={0}
|
|
1929
|
-
emptySearchResultsText="Old copy"
|
|
1930
|
-
emptySearchText="New copy"
|
|
1931
|
-
/>,
|
|
1932
|
-
);
|
|
1933
|
-
await searchForNothing();
|
|
1934
|
-
|
|
1935
|
-
await waitFor(() => {
|
|
1936
|
-
expect(screen.getByText('New copy')).toBeInTheDocument();
|
|
1886
|
+
expect(screen.getByText('No results found')).toBeInTheDocument();
|
|
1937
1887
|
});
|
|
1938
|
-
expect(screen.queryByText('
|
|
1939
|
-
expect(warn).toHaveBeenCalledWith(
|
|
1940
|
-
expect.stringContaining(
|
|
1941
|
-
'Tokenizer: `emptySearchResultsText` and `emptySearchText` are both set',
|
|
1942
|
-
),
|
|
1888
|
+
expect(screen.queryByText('Nobody found')).not.toBeInTheDocument();
|
|
1889
|
+
expect(warn).not.toHaveBeenCalledWith(
|
|
1890
|
+
expect.stringContaining('is deprecated'),
|
|
1943
1891
|
);
|
|
1944
1892
|
warn.mockRestore();
|
|
1893
|
+
error.mockRestore();
|
|
1945
1894
|
});
|
|
1946
1895
|
});
|
|
1947
1896
|
|
|
@@ -68,7 +68,6 @@ import type {SearchableItem, SearchSource} from '../Typeahead/types';
|
|
|
68
68
|
import {mergeProps} from '../utils';
|
|
69
69
|
import {themeProps} from '../utils/themeProps';
|
|
70
70
|
import {useTranslator} from '../i18n';
|
|
71
|
-
import {useRenamedProp} from '../hooks/useRenamedProp';
|
|
72
71
|
|
|
73
72
|
// Re-export status types for convenience
|
|
74
73
|
export type {
|
|
@@ -206,18 +205,6 @@ export interface TokenizerProps<T extends SearchableItem> extends Omit<
|
|
|
206
205
|
* @default 'No results found'
|
|
207
206
|
*/
|
|
208
207
|
emptySearchText?: ReactNode;
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Text shown when no results found.
|
|
212
|
-
* @default 'No results found'
|
|
213
|
-
* @deprecated `DEP-0001`. Renamed to `emptySearchText`, which takes a
|
|
214
|
-
* `ReactNode` rather than a `string` — every existing value stays valid
|
|
215
|
-
* (`spec:AST-056` FR1, FR7). Still works exactly as released;
|
|
216
|
-
* `emptySearchText` wins when both are set. Removal is `CLN-0001`, in a
|
|
217
|
-
* later minor whose frozen manifest carries both ids (`spec:AST-017`
|
|
218
|
-
* FR31).
|
|
219
|
-
*/
|
|
220
|
-
emptySearchResultsText?: string;
|
|
221
208
|
/** Whether the input is disabled. @default false */
|
|
222
209
|
isDisabled?: boolean;
|
|
223
210
|
/**
|
|
@@ -577,8 +564,7 @@ export function Tokenizer<T extends SearchableItem>({
|
|
|
577
564
|
maxMenuItems,
|
|
578
565
|
menuWidth,
|
|
579
566
|
minQueryLength,
|
|
580
|
-
|
|
581
|
-
emptySearchText: emptySearchTextFromProps,
|
|
567
|
+
emptySearchText,
|
|
582
568
|
isDisabled = false,
|
|
583
569
|
htmlName,
|
|
584
570
|
disabledMessage,
|
|
@@ -633,16 +619,6 @@ export function Tokenizer<T extends SearchableItem>({
|
|
|
633
619
|
isEnabled: showsDisabledMessage,
|
|
634
620
|
});
|
|
635
621
|
|
|
636
|
-
// The replacement wins, the released name keeps working, and development
|
|
637
|
-
// says which one was read (`spec:AST-056` FR7, `spec:AST-017` FR28).
|
|
638
|
-
const emptySearchText = useRenamedProp<ReactNode>({
|
|
639
|
-
component: 'Tokenizer',
|
|
640
|
-
deprecated: 'emptySearchResultsText',
|
|
641
|
-
deprecatedValue: emptySearchResultsText,
|
|
642
|
-
replacement: 'emptySearchText',
|
|
643
|
-
value: emptySearchTextFromProps,
|
|
644
|
-
});
|
|
645
|
-
|
|
646
622
|
useImperativeHandle(handleRef, () => ({
|
|
647
623
|
focus() {
|
|
648
624
|
inputRef.current?.focus();
|
|
@@ -12,7 +12,12 @@
|
|
|
12
12
|
import {describe, it, expect, vi} from 'vitest';
|
|
13
13
|
import {render, screen} from '@testing-library/react';
|
|
14
14
|
import userEvent from '@testing-library/user-event';
|
|
15
|
+
import * as stylex from '@stylexjs/stylex';
|
|
15
16
|
import {Toolbar} from './Toolbar';
|
|
17
|
+
import {
|
|
18
|
+
paddingBlockStyles,
|
|
19
|
+
paddingInlineStyles,
|
|
20
|
+
} from '../Layout/padding.stylex';
|
|
16
21
|
|
|
17
22
|
describe('Toolbar', () => {
|
|
18
23
|
it('renders with toolbar role', () => {
|
|
@@ -148,6 +153,30 @@ describe('Toolbar', () => {
|
|
|
148
153
|
expect(toolbar).toHaveAttribute('data-size', 'sm');
|
|
149
154
|
});
|
|
150
155
|
|
|
156
|
+
it.each([
|
|
157
|
+
['sm', 2],
|
|
158
|
+
['md', 2],
|
|
159
|
+
] as const)(
|
|
160
|
+
'uses the %s block inset without overriding container inline padding',
|
|
161
|
+
(size, blockPadding) => {
|
|
162
|
+
const {container} = render(<Toolbar label="Actions" size={size} />);
|
|
163
|
+
const sectionInner = container.querySelector('.astryx-section');
|
|
164
|
+
const expectedBlockClasses = stylex
|
|
165
|
+
.props(paddingBlockStyles[blockPadding])
|
|
166
|
+
.className?.split(' ')
|
|
167
|
+
.filter(Boolean);
|
|
168
|
+
const compactInlineClasses = stylex
|
|
169
|
+
.props(paddingInlineStyles[1])
|
|
170
|
+
.className?.split(' ')
|
|
171
|
+
.filter(Boolean);
|
|
172
|
+
|
|
173
|
+
expect(sectionInner).toBeInTheDocument();
|
|
174
|
+
expect(expectedBlockClasses).not.toHaveLength(0);
|
|
175
|
+
expect(sectionInner).toHaveClass(...(expectedBlockClasses ?? []));
|
|
176
|
+
expect(sectionInner).not.toHaveClass(...(compactInlineClasses ?? []));
|
|
177
|
+
},
|
|
178
|
+
);
|
|
179
|
+
|
|
151
180
|
it('defaults to md size', () => {
|
|
152
181
|
render(<Toolbar label="Actions" />);
|
|
153
182
|
const toolbar = screen.getByRole('toolbar');
|
|
@@ -165,13 +165,6 @@ export const docs = {
|
|
|
165
165
|
'Content shown when the query matched nothing. Takes a ReactNode, so the dead end can carry a link or a create row. Announced in a polite live region as the text it renders, read from the DOM; aria-hidden parts stay out of both, and content that renders no text announces nothing. null counts as not given, like undefined, and falls through to the default; pass an empty string to render nothing.',
|
|
166
166
|
default: "'No results found'",
|
|
167
167
|
},
|
|
168
|
-
{
|
|
169
|
-
name: 'emptySearchResultsText',
|
|
170
|
-
type: 'string',
|
|
171
|
-
description:
|
|
172
|
-
'Deprecated: renamed to emptySearchText, which takes a ReactNode rather than a string, so every existing value stays valid. Still works exactly as released; emptySearchText wins when both are set.',
|
|
173
|
-
default: "'No results found'",
|
|
174
|
-
},
|
|
175
168
|
{
|
|
176
169
|
name: 'isDisabled',
|
|
177
170
|
type: 'boolean',
|
|
@@ -331,13 +324,6 @@ export const docsZh = {
|
|
|
331
324
|
'查询无匹配结果时显示的内容。接受 ReactNode,因此可在无结果处放置链接或创建入口。会从 DOM 读取其渲染出的文本在礼貌性实时区域中播报;aria-hidden 的部分两处都不包含,不渲染任何文本的内容则不会播报。null 与 undefined 同样视为未提供,将回退到默认值;若要不渲染任何内容,请传入空字符串。',
|
|
332
325
|
default: "'No results found'",
|
|
333
326
|
},
|
|
334
|
-
{
|
|
335
|
-
name: 'emptySearchResultsText',
|
|
336
|
-
type: 'string',
|
|
337
|
-
description:
|
|
338
|
-
'已弃用:改名为 emptySearchText,其类型由 string 放宽为 ReactNode,原有取值全部仍然有效。仍按已发布行为工作;两者同时设置时以 emptySearchText 为准。',
|
|
339
|
-
default: "'No results found'",
|
|
340
|
-
},
|
|
341
327
|
{
|
|
342
328
|
name: 'isDisabled',
|
|
343
329
|
type: 'boolean',
|
|
@@ -479,8 +465,6 @@ export const docsDense = {
|
|
|
479
465
|
minQueryLength:
|
|
480
466
|
'Min query length before searching. Menu stays closed below it.',
|
|
481
467
|
emptySearchText: 'Content when the query matched nothing. ReactNode.',
|
|
482
|
-
emptySearchResultsText:
|
|
483
|
-
'deprecated, use emptySearchText (ReactNode). still works as released; emptySearchText wins if both set.',
|
|
484
468
|
isDisabled: 'Whether input disabled.',
|
|
485
469
|
isFocusableDisabled:
|
|
486
470
|
'Keeps disabled input focusable and blocks text entry; an already-open highlight can still be selected with Enter.',
|
|
@@ -33,7 +33,6 @@ import type {StyleXStyles} from '@stylexjs/stylex';
|
|
|
33
33
|
import {usePopover} from '../Popover/usePopover';
|
|
34
34
|
import {useAnnounce} from '../hooks/useAnnounce';
|
|
35
35
|
import {useAnnounceRenderedText} from '../hooks/useAnnounceRenderedText';
|
|
36
|
-
import {useRenamedProp} from '../hooks/useRenamedProp';
|
|
37
36
|
import {useHighlightedOptionScroll} from '../hooks/useHighlightedOptionScroll';
|
|
38
37
|
import {useIsomorphicLayoutEffect} from '../hooks/useIsomorphicLayoutEffect';
|
|
39
38
|
import {isImeKeyEvent} from '../utils/ime';
|
|
@@ -143,18 +142,6 @@ export interface BaseTypeaheadProps<T extends SearchableItem> extends Omit<
|
|
|
143
142
|
*/
|
|
144
143
|
emptySearchText?: ReactNode;
|
|
145
144
|
|
|
146
|
-
/**
|
|
147
|
-
* Text shown when no results found.
|
|
148
|
-
* @default 'No results found'
|
|
149
|
-
* @deprecated `DEP-0003`. Renamed to `emptySearchText`, which takes a
|
|
150
|
-
* `ReactNode` rather than a `string` — every existing value stays valid
|
|
151
|
-
* (`spec:AST-056` FR1, FR7). Still works exactly as released;
|
|
152
|
-
* `emptySearchText` wins when both are set. Removal is `CLN-0003`, in a
|
|
153
|
-
* later minor whose frozen manifest carries both ids (`spec:AST-017`
|
|
154
|
-
* FR31).
|
|
155
|
-
*/
|
|
156
|
-
emptySearchResultsText?: string;
|
|
157
|
-
|
|
158
145
|
/**
|
|
159
146
|
* Whether the input is disabled.
|
|
160
147
|
* @default false
|
|
@@ -465,7 +452,6 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
|
|
|
465
452
|
maxMenuItems = 10,
|
|
466
453
|
menuWidth,
|
|
467
454
|
minQueryLength = 1,
|
|
468
|
-
emptySearchResultsText: deprecatedEmptySearchResultsText,
|
|
469
455
|
emptySearchText: emptySearchTextFromProps,
|
|
470
456
|
isDisabled = false,
|
|
471
457
|
isFocusableDisabled = false,
|
|
@@ -500,13 +486,7 @@ export const BaseTypeahead = function BaseTypeahead<T extends SearchableItem>({
|
|
|
500
486
|
const placeholder =
|
|
501
487
|
placeholderFromProps ?? t('@astryx.typeahead.searchPlaceholder');
|
|
502
488
|
const emptySearchText =
|
|
503
|
-
|
|
504
|
-
component: 'BaseTypeahead',
|
|
505
|
-
deprecated: 'emptySearchResultsText',
|
|
506
|
-
deprecatedValue: deprecatedEmptySearchResultsText,
|
|
507
|
-
replacement: 'emptySearchText',
|
|
508
|
-
value: emptySearchTextFromProps,
|
|
509
|
-
}) ?? t('@astryx.typeahead.emptySearchResults');
|
|
489
|
+
emptySearchTextFromProps ?? t('@astryx.typeahead.emptySearchResults');
|
|
510
490
|
// The empty-state row carries the message visually, and the live region has
|
|
511
491
|
// to speak the same words. `emptySearchText` takes a ReactNode, so they are
|
|
512
492
|
// read off the rendered row after it renders rather than guessed from the
|
|
@@ -230,13 +230,6 @@ export const docs = {
|
|
|
230
230
|
'Content shown when the query matched nothing. Takes a ReactNode, so the dead end can carry a link or a create row. Announced in a polite live region as the text it renders, read from the DOM; aria-hidden parts stay out of both, and content that renders no text announces nothing. null counts as not given, like undefined, and falls through to the default; pass an empty string to render nothing.',
|
|
231
231
|
default: "'No results found'",
|
|
232
232
|
},
|
|
233
|
-
{
|
|
234
|
-
name: 'emptySearchResultsText',
|
|
235
|
-
type: 'string',
|
|
236
|
-
description:
|
|
237
|
-
'Deprecated: renamed to emptySearchText, which takes a ReactNode rather than a string, so every existing value stays valid. Still works exactly as released; emptySearchText wins when both are set.',
|
|
238
|
-
default: "'No results found'",
|
|
239
|
-
},
|
|
240
233
|
{
|
|
241
234
|
name: 'hasAutoFocus',
|
|
242
235
|
type: 'boolean',
|
|
@@ -239,7 +239,7 @@ describe('BaseTypeahead', () => {
|
|
|
239
239
|
value={null}
|
|
240
240
|
onChange={() => {}}
|
|
241
241
|
debounceMs={0}
|
|
242
|
-
|
|
242
|
+
emptySearchText="No results found"
|
|
243
243
|
/>,
|
|
244
244
|
);
|
|
245
245
|
const input = screen.getByRole('combobox');
|
|
@@ -260,7 +260,7 @@ describe('BaseTypeahead', () => {
|
|
|
260
260
|
value={null}
|
|
261
261
|
onChange={() => {}}
|
|
262
262
|
debounceMs={0}
|
|
263
|
-
|
|
263
|
+
emptySearchText="No results found"
|
|
264
264
|
/>,
|
|
265
265
|
);
|
|
266
266
|
const input = screen.getByRole('combobox');
|
|
@@ -2189,9 +2189,9 @@ describe('the value is bounded by the content lane', () => {
|
|
|
2189
2189
|
});
|
|
2190
2190
|
|
|
2191
2191
|
// `emptySearchResultsText` was renamed to `emptySearchText` and widened from
|
|
2192
|
-
// `string` to `ReactNode` (`spec:AST-056` FR1).
|
|
2193
|
-
//
|
|
2194
|
-
//
|
|
2192
|
+
// `string` to `ReactNode` (`spec:AST-056` FR1). The overlap ended in a minor
|
|
2193
|
+
// release (`CLN-0001`–`CLN-0004`, `spec:AST-017` FR31): the old name is no
|
|
2194
|
+
// longer part of the type and is no longer read.
|
|
2195
2195
|
describe('emptySearchText rename', () => {
|
|
2196
2196
|
function searchForNothing() {
|
|
2197
2197
|
fireEvent.change(screen.getByRole('combobox'), {
|
|
@@ -2222,8 +2222,10 @@ describe('emptySearchText rename', () => {
|
|
|
2222
2222
|
});
|
|
2223
2223
|
});
|
|
2224
2224
|
|
|
2225
|
-
it('
|
|
2225
|
+
it('no longer reads the removed emptySearchResultsText (CLN-0002)', async () => {
|
|
2226
2226
|
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
2227
|
+
// The unknown prop reaches the root element, which React reports.
|
|
2228
|
+
const error = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
2227
2229
|
render(
|
|
2228
2230
|
<Typeahead
|
|
2229
2231
|
label="Fruit"
|
|
@@ -2231,47 +2233,21 @@ describe('emptySearchText rename', () => {
|
|
|
2231
2233
|
value={null}
|
|
2232
2234
|
onChange={() => {}}
|
|
2233
2235
|
debounceMs={0}
|
|
2236
|
+
// @ts-expect-error -- removed in the minor release (CLN-0002)
|
|
2234
2237
|
emptySearchResultsText="Nothing ripe"
|
|
2235
2238
|
/>,
|
|
2236
2239
|
);
|
|
2237
2240
|
searchForNothing();
|
|
2238
2241
|
|
|
2239
2242
|
await waitFor(() => {
|
|
2240
|
-
expect(screen.getByText('
|
|
2243
|
+
expect(screen.getByText('No results found')).toBeInTheDocument();
|
|
2241
2244
|
});
|
|
2242
|
-
expect(
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
),
|
|
2246
|
-
);
|
|
2247
|
-
warn.mockRestore();
|
|
2248
|
-
});
|
|
2249
|
-
|
|
2250
|
-
it('lets the new name win when both are set, and warns', async () => {
|
|
2251
|
-
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
2252
|
-
render(
|
|
2253
|
-
<Typeahead
|
|
2254
|
-
label="Fruit"
|
|
2255
|
-
searchSource={fruitSource}
|
|
2256
|
-
value={null}
|
|
2257
|
-
onChange={() => {}}
|
|
2258
|
-
debounceMs={0}
|
|
2259
|
-
emptySearchResultsText="Old copy"
|
|
2260
|
-
emptySearchText="New copy"
|
|
2261
|
-
/>,
|
|
2262
|
-
);
|
|
2263
|
-
searchForNothing();
|
|
2264
|
-
|
|
2265
|
-
await waitFor(() => {
|
|
2266
|
-
expect(screen.getByText('New copy')).toBeInTheDocument();
|
|
2267
|
-
});
|
|
2268
|
-
expect(screen.queryByText('Old copy')).not.toBeInTheDocument();
|
|
2269
|
-
expect(warn).toHaveBeenCalledWith(
|
|
2270
|
-
expect.stringContaining(
|
|
2271
|
-
'Typeahead: `emptySearchResultsText` and `emptySearchText` are both set',
|
|
2272
|
-
),
|
|
2245
|
+
expect(screen.queryByText('Nothing ripe')).not.toBeInTheDocument();
|
|
2246
|
+
expect(warn).not.toHaveBeenCalledWith(
|
|
2247
|
+
expect.stringContaining('is deprecated'),
|
|
2273
2248
|
);
|
|
2274
2249
|
warn.mockRestore();
|
|
2250
|
+
error.mockRestore();
|
|
2275
2251
|
});
|
|
2276
2252
|
});
|
|
2277
2253
|
|
|
@@ -2301,14 +2277,16 @@ describe('emptySearchText rename', () => {
|
|
|
2301
2277
|
});
|
|
2302
2278
|
});
|
|
2303
2279
|
|
|
2304
|
-
it('announces the
|
|
2305
|
-
|
|
2280
|
+
it('no longer announces the removed emptySearchResultsText (CLN-0003)', async () => {
|
|
2281
|
+
// The unknown prop reaches the root element, which React reports.
|
|
2282
|
+
const error = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
2306
2283
|
render(
|
|
2307
2284
|
<BaseTypeahead
|
|
2308
2285
|
searchSource={fruitSource}
|
|
2309
2286
|
value={null}
|
|
2310
2287
|
onChange={() => {}}
|
|
2311
2288
|
debounceMs={0}
|
|
2289
|
+
// @ts-expect-error -- removed in the minor release (CLN-0003)
|
|
2312
2290
|
emptySearchResultsText="Nothing ripe"
|
|
2313
2291
|
/>,
|
|
2314
2292
|
);
|
|
@@ -2317,9 +2295,13 @@ describe('emptySearchText rename', () => {
|
|
|
2317
2295
|
await waitFor(() => {
|
|
2318
2296
|
expect(
|
|
2319
2297
|
document.querySelector('[data-astryx-live-region="polite"]'),
|
|
2320
|
-
).toHaveTextContent('
|
|
2298
|
+
).toHaveTextContent('No results found');
|
|
2321
2299
|
});
|
|
2322
|
-
|
|
2300
|
+
expect(
|
|
2301
|
+
document.querySelector('[data-astryx-live-region="polite"]')
|
|
2302
|
+
?.textContent,
|
|
2303
|
+
).not.toContain('Nothing ripe');
|
|
2304
|
+
error.mockRestore();
|
|
2323
2305
|
});
|
|
2324
2306
|
});
|
|
2325
2307
|
});
|
|
@@ -70,7 +70,6 @@ import {themeProps} from '../utils/themeProps';
|
|
|
70
70
|
import {useTranslator} from '../i18n';
|
|
71
71
|
|
|
72
72
|
import {useMergedRefs} from '../hooks/useMergedRefs';
|
|
73
|
-
import {useRenamedProp} from '../hooks/useRenamedProp';
|
|
74
73
|
export type {
|
|
75
74
|
InputStatus as TypeaheadStatus,
|
|
76
75
|
InputStatusType as TypeaheadStatusType,
|
|
@@ -160,18 +159,6 @@ export interface TypeaheadProps<T extends SearchableItem> extends Omit<
|
|
|
160
159
|
* @default 'No results found'
|
|
161
160
|
*/
|
|
162
161
|
emptySearchText?: ReactNode;
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Text shown when no results found.
|
|
166
|
-
* @default 'No results found'
|
|
167
|
-
* @deprecated `DEP-0002`. Renamed to `emptySearchText`, which takes a
|
|
168
|
-
* `ReactNode` rather than a `string` — every existing value stays valid
|
|
169
|
-
* (`spec:AST-056` FR1, FR7). Still works exactly as released;
|
|
170
|
-
* `emptySearchText` wins when both are set. Removal is `CLN-0002`, in a
|
|
171
|
-
* later minor whose frozen manifest carries both ids (`spec:AST-017`
|
|
172
|
-
* FR31).
|
|
173
|
-
*/
|
|
174
|
-
emptySearchResultsText?: string;
|
|
175
162
|
/** Whether the input is disabled. @default false */
|
|
176
163
|
isDisabled?: boolean;
|
|
177
164
|
/**
|
|
@@ -399,8 +386,7 @@ export function Typeahead<T extends SearchableItem>({
|
|
|
399
386
|
hasEntriesOnFocus,
|
|
400
387
|
maxMenuItems,
|
|
401
388
|
minQueryLength,
|
|
402
|
-
|
|
403
|
-
emptySearchText: emptySearchTextFromProps,
|
|
389
|
+
emptySearchText,
|
|
404
390
|
isDisabled = false,
|
|
405
391
|
disabledMessage,
|
|
406
392
|
hasClear = true,
|
|
@@ -435,16 +421,6 @@ export function Typeahead<T extends SearchableItem>({
|
|
|
435
421
|
const statusMessageId = useId();
|
|
436
422
|
const inputGroup = useInputGroup();
|
|
437
423
|
|
|
438
|
-
// The replacement wins, the released name keeps working, and development
|
|
439
|
-
// says which one was read (`spec:AST-056` FR7, `spec:AST-017` FR28).
|
|
440
|
-
const emptySearchText = useRenamedProp<ReactNode>({
|
|
441
|
-
component: 'Typeahead',
|
|
442
|
-
deprecated: 'emptySearchResultsText',
|
|
443
|
-
deprecatedValue: emptySearchResultsText,
|
|
444
|
-
replacement: 'emptySearchText',
|
|
445
|
-
value: emptySearchTextFromProps,
|
|
446
|
-
});
|
|
447
|
-
|
|
448
424
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
|
449
425
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
450
426
|
const tokenRef = useRef<HTMLElement>(null);
|
|
@@ -91,8 +91,12 @@ function isVisiblyFocusable(el: HTMLElement): boolean {
|
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
93
|
* Get all focusable elements within a container.
|
|
94
|
+
*
|
|
95
|
+
* Exported for menus that place their own Tab stops inside a trapped layer
|
|
96
|
+
* (DropdownMenu's header and footer), so they judge reachability exactly as
|
|
97
|
+
* the trap does. Not re-exported from the package's `hooks` entry.
|
|
94
98
|
*/
|
|
95
|
-
function getFocusableElements(container: HTMLElement): HTMLElement[] {
|
|
99
|
+
export function getFocusableElements(container: HTMLElement): HTMLElement[] {
|
|
96
100
|
return Array.from(
|
|
97
101
|
container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR),
|
|
98
102
|
).filter(isVisiblyFocusable);
|
|
@@ -56,12 +56,12 @@ export interface RenamedPropOptions<T> {
|
|
|
56
56
|
*
|
|
57
57
|
* @example
|
|
58
58
|
* ```
|
|
59
|
-
* const
|
|
60
|
-
* component: '
|
|
61
|
-
* deprecated: '
|
|
62
|
-
* deprecatedValue:
|
|
63
|
-
* replacement: '
|
|
64
|
-
* value:
|
|
59
|
+
* const label = useRenamedProp({
|
|
60
|
+
* component: 'Example',
|
|
61
|
+
* deprecated: 'oldLabel',
|
|
62
|
+
* deprecatedValue: oldLabel,
|
|
63
|
+
* replacement: 'label',
|
|
64
|
+
* value: labelFromProps,
|
|
65
65
|
* });
|
|
66
66
|
* ```
|
|
67
67
|
*/
|