@astryxdesign/core 0.4.0 → 0.4.1
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 +39 -0
- package/dist/AspectRatio/AspectRatio.d.ts.map +1 -1
- package/dist/AspectRatio/AspectRatio.js +1 -2
- package/dist/AspectRatio/index.d.ts.map +1 -1
- package/dist/AspectRatio/index.js +1 -2
- package/dist/Badge/Badge.d.ts.map +1 -1
- package/dist/Badge/Badge.js +1 -2
- package/dist/Badge/index.d.ts.map +1 -1
- package/dist/Badge/index.js +1 -2
- package/dist/Banner/Banner.d.ts +3 -0
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +17 -3
- package/dist/Blockquote/Blockquote.d.ts.map +1 -1
- package/dist/Blockquote/Blockquote.js +1 -2
- package/dist/Blockquote/index.d.ts.map +1 -1
- package/dist/Blockquote/index.js +1 -2
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +2 -2
- package/dist/Card/Card.d.ts.map +1 -1
- package/dist/Card/Card.js +1 -2
- package/dist/Card/index.d.ts.map +1 -1
- package/dist/Card/index.js +1 -2
- package/dist/Center/Center.d.ts.map +1 -1
- package/dist/Center/Center.js +1 -2
- package/dist/Center/index.d.ts.map +1 -1
- package/dist/Center/index.js +1 -2
- package/dist/Code/Code.d.ts.map +1 -1
- package/dist/Code/Code.js +1 -2
- package/dist/CommandPalette/CommandPalette.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPalette.js +5 -0
- package/dist/ComplexSelector/ComplexSelector.d.ts.map +1 -1
- package/dist/ComplexSelector/ComplexSelector.js +7 -8
- package/dist/DateInput/DateInput.d.ts.map +1 -1
- package/dist/DateInput/DateInput.js +23 -10
- package/dist/DateRangeInput/DateRangeInput.d.ts.map +1 -1
- package/dist/DateRangeInput/DateRangeInput.js +44 -16
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +32 -13
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuRadioGroup.js +10 -3
- package/dist/Field/PanelSearchInput.d.ts +81 -0
- package/dist/Field/PanelSearchInput.d.ts.map +1 -0
- package/dist/Field/PanelSearchInput.js +151 -0
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/FileInput/FileInput.js +21 -10
- package/dist/Grid/Grid.d.ts.map +1 -1
- package/dist/Grid/Grid.js +1 -2
- package/dist/Grid/index.d.ts.map +1 -1
- package/dist/Grid/index.js +1 -2
- package/dist/Icon/globalIconRegistry.d.ts +5 -4
- package/dist/Icon/globalIconRegistry.d.ts.map +1 -1
- package/dist/Icon/globalIconRegistry.js +5 -4
- package/dist/Indicator/index.d.ts +1 -1
- package/dist/Indicator/index.d.ts.map +1 -1
- package/dist/Indicator/types.d.ts +10 -0
- package/dist/Indicator/types.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +24 -50
- package/dist/MultiSelector/MultiSelector.d.ts +8 -1
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +134 -102
- package/dist/Popover/usePopover.d.ts +15 -0
- package/dist/Popover/usePopover.d.ts.map +1 -1
- package/dist/Popover/usePopover.js +14 -2
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +2 -3
- package/dist/Section/Section.d.ts.map +1 -1
- package/dist/Section/Section.js +1 -2
- package/dist/Section/index.d.ts.map +1 -1
- package/dist/Section/index.js +1 -2
- package/dist/Selector/Selector.d.ts +10 -0
- package/dist/Selector/Selector.d.ts.map +1 -1
- package/dist/Selector/Selector.js +135 -95
- package/dist/Skeleton/Skeleton.d.ts.map +1 -1
- package/dist/Skeleton/Skeleton.js +1 -2
- package/dist/Skeleton/index.d.ts.map +1 -1
- package/dist/Skeleton/index.js +1 -2
- package/dist/Slider/Slider.d.ts.map +1 -1
- package/dist/Slider/Slider.js +2 -6
- package/dist/Switch/Switch.d.ts.map +1 -1
- package/dist/Switch/Switch.js +3 -3
- package/dist/Table/TableRow.d.ts +1 -1
- package/dist/Table/TableRow.d.ts.map +1 -1
- package/dist/Table/TableRow.js +4 -2
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +7 -2
- package/dist/Timestamp/Timestamp.d.ts.map +1 -1
- package/dist/Timestamp/Timestamp.js +14 -3
- package/dist/Timestamp/formatInstant.d.ts +11 -1
- package/dist/Timestamp/formatInstant.d.ts.map +1 -1
- package/dist/Timestamp/formatInstant.js +3 -1
- package/dist/VisuallyHidden/VisuallyHidden.d.ts.map +1 -1
- package/dist/VisuallyHidden/VisuallyHidden.js +1 -2
- package/dist/VisuallyHidden/index.d.ts.map +1 -1
- package/dist/VisuallyHidden/index.js +1 -2
- package/dist/astryx.css +18 -17
- package/dist/astryx.umd.js +45 -45
- package/dist/astryx.umd.js.map +4 -4
- package/dist/hooks/useInputStatusIcon.d.ts.map +1 -1
- package/dist/hooks/useInputStatusIcon.js +17 -4
- package/dist/theme/defineTheme.d.ts +2 -2
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/defineTheme.js +2 -1
- package/dist/theme/index.d.ts +1 -1
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +1 -1
- package/dist/theme/tokens.stylex.d.ts +12 -0
- package/dist/theme/tokens.stylex.d.ts.map +1 -1
- package/dist/theme/tokens.stylex.js +23 -0
- package/dist/utils/focusOutline.stylex.d.ts +34 -19
- package/dist/utils/focusOutline.stylex.d.ts.map +1 -1
- package/dist/utils/focusOutline.stylex.js +46 -33
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +5 -1
- package/dist/utils/interactionModality.d.ts +34 -0
- package/dist/utils/interactionModality.d.ts.map +1 -0
- package/dist/utils/interactionModality.js +70 -0
- package/locales/en.json +40 -0
- package/locales/pseudo.json +30 -0
- package/package.json +2 -2
- package/src/AppShell/AppShell.doc.mjs +3 -0
- package/src/AspectRatio/AspectRatio.tsx +0 -2
- package/src/AspectRatio/index.ts +0 -2
- package/src/Badge/Badge.tsx +0 -2
- package/src/Badge/index.ts +0 -2
- package/src/Banner/Banner.test.tsx +58 -1
- package/src/Banner/Banner.tsx +24 -5
- package/src/Blockquote/Blockquote.tsx +0 -2
- package/src/Blockquote/index.ts +0 -2
- package/src/Button/Button.doc.mjs +2 -2
- package/src/Button/Button.tsx +5 -4
- package/src/Card/Card.tsx +0 -2
- package/src/Card/index.ts +0 -2
- package/src/Center/Center.tsx +0 -2
- package/src/Center/index.ts +0 -2
- package/src/CheckboxInput/CheckboxInput.test.tsx +8 -3
- package/src/Code/Code.tsx +0 -2
- package/src/CommandPalette/CommandPalette.test.tsx +54 -0
- package/src/CommandPalette/CommandPalette.tsx +5 -0
- package/src/ComplexSelector/ComplexSelector.test.tsx +15 -6
- package/src/ComplexSelector/ComplexSelector.tsx +6 -10
- package/src/DateInput/DateInput.test.tsx +19 -0
- package/src/DateInput/DateInput.tsx +3 -6
- package/src/DateRangeInput/DateRangeInput.tsx +3 -9
- package/src/DateTimeInput/DateTimeInput.test.tsx +79 -1
- package/src/DateTimeInput/DateTimeInput.tsx +23 -9
- package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +2 -1
- package/src/Field/PanelSearchInput.tsx +271 -0
- package/src/FileInput/FileInput.tsx +21 -8
- package/src/Grid/Grid.tsx +0 -2
- package/src/Grid/index.ts +0 -2
- package/src/Icon/globalIconRegistry.tsx +5 -4
- package/src/Indicator/Indicator.doc.mjs +1 -1
- package/src/Indicator/index.ts +1 -0
- package/src/Indicator/types.ts +11 -0
- package/src/Lightbox/Lightbox.tsx +2 -11
- package/src/MultiSelector/MultiSelector.doc.mjs +9 -0
- package/src/MultiSelector/MultiSelector.test.tsx +135 -5
- package/src/MultiSelector/MultiSelector.tsx +170 -127
- package/src/Popover/Popover.doc.mjs +2 -0
- package/src/Popover/usePopover.tsx +37 -4
- package/src/ProgressBar/ProgressBar.tsx +2 -8
- package/src/Section/Section.tsx +0 -2
- package/src/Section/index.ts +0 -2
- package/src/Selector/Selector.doc.mjs +10 -0
- package/src/Selector/Selector.test.tsx +386 -2
- package/src/Selector/Selector.tsx +182 -122
- package/src/Skeleton/Skeleton.tsx +0 -2
- package/src/Skeleton/index.ts +0 -2
- package/src/Slider/Slider.tsx +2 -11
- package/src/Switch/Switch.tsx +10 -2
- package/src/Table/Table.test.tsx +31 -0
- package/src/Table/TableRow.tsx +6 -0
- package/src/TimeInput/TimeInput.test.tsx +54 -0
- package/src/TimeInput/TimeInput.tsx +17 -2
- package/src/Timestamp/Timestamp.test.tsx +91 -14
- package/src/Timestamp/Timestamp.tsx +14 -2
- package/src/Timestamp/formatInstant.ts +16 -1
- package/src/TopNav/TopNavMegaMenu.doc.mjs +5 -0
- package/src/TreeList/TreeList.test.tsx +3 -2
- package/src/VisuallyHidden/VisuallyHidden.tsx +0 -2
- package/src/VisuallyHidden/index.ts +0 -2
- package/src/hooks/useInputStatusIcon.tsx +8 -8
- package/src/serverSafeComponents.test.ts +665 -0
- package/src/theme/defineTheme.ts +3 -0
- package/src/theme/generateThemeRules.test.ts +17 -0
- package/src/theme/index.ts +2 -0
- package/src/theme/tokens.stylex.ts +18 -0
- package/src/theme/tokens.test.ts +37 -0
- package/src/utils/focusOutline.stylex.ts +37 -23
- package/src/utils/index.ts +4 -0
- package/src/utils/interactionModality.ts +74 -0
|
@@ -110,12 +110,12 @@ function getThemeIconOverrides(
|
|
|
110
110
|
* ```
|
|
111
111
|
*
|
|
112
112
|
* Libraries may also register their own extension keys (any string), so a
|
|
113
|
-
* theme can override them the same way it overrides built-in icons
|
|
113
|
+
* theme can override them the same way it overrides built-in icons. A library
|
|
114
|
+
* that ships its own icons registers them by key, then resolves with
|
|
115
|
+
* `getIcon('richtext:bold')`.
|
|
114
116
|
* @example
|
|
115
117
|
* ```
|
|
116
|
-
* // In a library that ships its own icons:
|
|
117
118
|
* registerIcons({ 'richtext:bold': <MyBoldIcon /> });
|
|
118
|
-
* // resolve with getIcon('richtext:bold')
|
|
119
119
|
* ```
|
|
120
120
|
*/
|
|
121
121
|
export function registerIcons(
|
|
@@ -191,9 +191,10 @@ export function getIcon(
|
|
|
191
191
|
* override the key via {@link registerIcons} without the library having to
|
|
192
192
|
* widen the core {@link IconName} union.
|
|
193
193
|
*
|
|
194
|
+
* The `fallback` is the library default, overridable by a theme registering the
|
|
195
|
+
* same key (for example `'richtext:bold'`).
|
|
194
196
|
* @example
|
|
195
197
|
* ```
|
|
196
|
-
* // Library default, overridable by a theme registering 'richtext:bold':
|
|
197
198
|
* getExtendedIcon('richtext:bold', <BoldGlyph />)
|
|
198
199
|
* ```
|
|
199
200
|
*/
|
|
@@ -52,7 +52,7 @@ export const docs = {
|
|
|
52
52
|
name: 'children',
|
|
53
53
|
type: 'ReactNode',
|
|
54
54
|
description:
|
|
55
|
-
'Rendered inside the chrome INSTEAD of the state mark. CheckboxInput passes its loading Spinner through this while a change action is pending, so a replacement indicator must render children when they will actually draw something
|
|
55
|
+
'Rendered inside the chrome INSTEAD of the state mark. CheckboxInput passes its loading Spinner through this while a change action is pending, so a replacement indicator must render children when they will actually draw something: use `isRenderable(children)`, never `children ?? mark`, because a host writes `children={isBusy && <Spinner/>}` and `false` slips straight past a nullish check and deletes the mark.',
|
|
56
56
|
},
|
|
57
57
|
],
|
|
58
58
|
},
|
package/src/Indicator/index.ts
CHANGED
package/src/Indicator/types.ts
CHANGED
|
@@ -67,6 +67,17 @@ export type IndicatorState<F extends IndicatorFamily = IndicatorFamily> =
|
|
|
67
67
|
/** Indicator size scale — matches the control sizes of the owning inputs. */
|
|
68
68
|
export type IndicatorSize = 'sm' | 'md';
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Which edge of its row an indicator sits on.
|
|
72
|
+
*
|
|
73
|
+
* Logical, not physical: `start` is the left edge in LTR and the right edge in
|
|
74
|
+
* RTL. Owned by the host component, not by the indicator — an indicator draws a
|
|
75
|
+
* picture and has no say in where the row puts it — which is why this is a prop
|
|
76
|
+
* on the components that lay out rows rather than part of
|
|
77
|
+
* {@link IndicatorProps}.
|
|
78
|
+
*/
|
|
79
|
+
export type IndicatorPosition = 'start' | 'end';
|
|
80
|
+
|
|
70
81
|
/**
|
|
71
82
|
* Props every indicator accepts.
|
|
72
83
|
*
|
|
@@ -35,6 +35,7 @@ import {useIsomorphicLayoutEffect} from '../hooks/useIsomorphicLayoutEffect';
|
|
|
35
35
|
import {mergeProps, mergeRefs, rtlStyles} from '../utils';
|
|
36
36
|
import type {BaseProps} from '../BaseProps';
|
|
37
37
|
import {themeProps} from '../utils/themeProps';
|
|
38
|
+
import {focusOutlineStyles} from '../utils/focusOutline.stylex';
|
|
38
39
|
import {useTranslator} from '../i18n';
|
|
39
40
|
|
|
40
41
|
/**
|
|
@@ -158,16 +159,6 @@ const styles = stylex.create({
|
|
|
158
159
|
'@media (hover: hover)': 'zoom-in',
|
|
159
160
|
},
|
|
160
161
|
},
|
|
161
|
-
zoomTarget: {
|
|
162
|
-
outline: {
|
|
163
|
-
default: 'none',
|
|
164
|
-
':focus-visible': `2px solid ${colorVars['--color-accent']}`,
|
|
165
|
-
},
|
|
166
|
-
outlineOffset: {
|
|
167
|
-
default: '0',
|
|
168
|
-
':focus-visible': '2px',
|
|
169
|
-
},
|
|
170
|
-
},
|
|
171
162
|
imageWrapperZoomed: {
|
|
172
163
|
cursor: 'grab',
|
|
173
164
|
},
|
|
@@ -641,7 +632,7 @@ export function Lightbox({
|
|
|
641
632
|
aria-label={isZoomTarget ? t('@astryx.lightbox.zoom') : undefined}
|
|
642
633
|
{...stylex.props(
|
|
643
634
|
styles.imageWrapper,
|
|
644
|
-
isZoomTarget &&
|
|
635
|
+
isZoomTarget && focusOutlineStyles.focusVisible,
|
|
645
636
|
!isVideo && hasZoom && !isZoomed && styles.imageWrapperZoomable,
|
|
646
637
|
!isVideo && isZoomed && styles.imageWrapperZoomed,
|
|
647
638
|
!isVideo && isDragging && styles.imageWrapperDragging,
|
|
@@ -27,6 +27,8 @@ export const docs = {
|
|
|
27
27
|
},
|
|
28
28
|
{className: 'astryx-multi-selector-clear-icon', deprecatedFor: 'input-clear-icon'},
|
|
29
29
|
{className: 'astryx-multi-selector-empty-state'},
|
|
30
|
+
{className: 'astryx-multi-selector-search'},
|
|
31
|
+
{className: 'astryx-multi-selector-section-heading'},
|
|
30
32
|
{
|
|
31
33
|
className: 'astryx-multi-selector-indicator-icon',
|
|
32
34
|
states: ['state'],
|
|
@@ -192,6 +194,13 @@ export const docs = {
|
|
|
192
194
|
description:
|
|
193
195
|
'Custom render function for each selectable option in the dropdown. Not called for dividers, sections, or the select-all row.',
|
|
194
196
|
},
|
|
197
|
+
{
|
|
198
|
+
name: 'indicatorPosition',
|
|
199
|
+
type: "'start' | 'end'",
|
|
200
|
+
description:
|
|
201
|
+
'Which edge of the option row carries the checkbox. end pushes it to the far edge of the row, including on the select-all row.',
|
|
202
|
+
default: "'start'",
|
|
203
|
+
},
|
|
195
204
|
{
|
|
196
205
|
name: 'width',
|
|
197
206
|
type: 'SizeValue',
|
|
@@ -1774,7 +1774,134 @@ describe('MultiSelector indicator (chevron) icon theme target', () => {
|
|
|
1774
1774
|
});
|
|
1775
1775
|
});
|
|
1776
1776
|
|
|
1777
|
+
describe('MultiSelector list structure', () => {
|
|
1778
|
+
it('does not draw a divider under select-all', async () => {
|
|
1779
|
+
const user = userEvent.setup();
|
|
1780
|
+
render(
|
|
1781
|
+
<MultiSelector
|
|
1782
|
+
label="Fruit"
|
|
1783
|
+
options={['Apple', 'Banana', 'Orange']}
|
|
1784
|
+
value={[]}
|
|
1785
|
+
onChange={() => {}}
|
|
1786
|
+
hasSelectAll
|
|
1787
|
+
/>,
|
|
1788
|
+
);
|
|
1789
|
+
await user.click(screen.getByRole('combobox'));
|
|
1790
|
+
// Select-all is the first row of the list, not a section of its own. No
|
|
1791
|
+
// option here declares a divider and there is no search row, so the panel
|
|
1792
|
+
// should contain no rule at all.
|
|
1793
|
+
expect(document.querySelectorAll('[role="separator"]')).toHaveLength(0);
|
|
1794
|
+
const [first] = screen.getAllByRole('option', h);
|
|
1795
|
+
expect(first).toHaveTextContent('Select all');
|
|
1796
|
+
});
|
|
1797
|
+
|
|
1798
|
+
it('renders a section title as a plain heading inside the group, not a divider', async () => {
|
|
1799
|
+
const user = userEvent.setup();
|
|
1800
|
+
render(
|
|
1801
|
+
<MultiSelector
|
|
1802
|
+
label="Fruit"
|
|
1803
|
+
options={[
|
|
1804
|
+
{
|
|
1805
|
+
type: 'section',
|
|
1806
|
+
title: 'Citrus',
|
|
1807
|
+
options: [
|
|
1808
|
+
{value: 'orange', label: 'Orange'},
|
|
1809
|
+
{value: 'lemon', label: 'Lemon'},
|
|
1810
|
+
],
|
|
1811
|
+
},
|
|
1812
|
+
]}
|
|
1813
|
+
value={[]}
|
|
1814
|
+
onChange={() => {}}
|
|
1815
|
+
/>,
|
|
1816
|
+
);
|
|
1817
|
+
await user.click(screen.getByRole('combobox'));
|
|
1818
|
+
|
|
1819
|
+
// A labeled Divider used to stand in for the heading; it rendered a
|
|
1820
|
+
// role="separator" as a direct child of the listbox and stacked a second
|
|
1821
|
+
// rule under the search row's own.
|
|
1822
|
+
expect(document.querySelectorAll('[role="separator"]')).toHaveLength(0);
|
|
1823
|
+
|
|
1824
|
+
const group = screen.getByRole('group', {name: 'Citrus', ...h});
|
|
1825
|
+
const heading = group.querySelector(
|
|
1826
|
+
'.astryx-multi-selector-section-heading',
|
|
1827
|
+
);
|
|
1828
|
+
expect(heading).toBeTruthy();
|
|
1829
|
+
expect(heading).toHaveTextContent('Citrus');
|
|
1830
|
+
// The group already carries the title as its accessible name, so the
|
|
1831
|
+
// visible heading must not announce it a second time.
|
|
1832
|
+
expect(heading).toHaveAttribute('aria-hidden', 'true');
|
|
1833
|
+
// ...and it precedes the options it heads.
|
|
1834
|
+
const [firstOption] = within(group).getAllByRole('option', h);
|
|
1835
|
+
expect(
|
|
1836
|
+
heading!.compareDocumentPosition(firstOption) &
|
|
1837
|
+
Node.DOCUMENT_POSITION_FOLLOWING,
|
|
1838
|
+
).toBeTruthy();
|
|
1839
|
+
});
|
|
1840
|
+
});
|
|
1841
|
+
|
|
1777
1842
|
describe('MultiSelector search affordances', () => {
|
|
1843
|
+
it('renders the search row seamlessly — no nested input box, a divider under it', async () => {
|
|
1844
|
+
const user = userEvent.setup();
|
|
1845
|
+
render(
|
|
1846
|
+
<MultiSelector
|
|
1847
|
+
label="Fruit"
|
|
1848
|
+
options={['Apple', 'Banana', 'Orange']}
|
|
1849
|
+
value={[]}
|
|
1850
|
+
onChange={() => {}}
|
|
1851
|
+
hasSearch
|
|
1852
|
+
/>,
|
|
1853
|
+
);
|
|
1854
|
+
await user.click(screen.getByRole('button', {name: 'Fruit'}));
|
|
1855
|
+
|
|
1856
|
+
const search = screen.getByRole('combobox', h);
|
|
1857
|
+
// The row is the outer gutter; the input sits inside the rounded field.
|
|
1858
|
+
const row = search.closest('.astryx-multi-selector-search');
|
|
1859
|
+
const field = search.parentElement;
|
|
1860
|
+
if (!row || !field) {
|
|
1861
|
+
throw new Error('search row not found');
|
|
1862
|
+
}
|
|
1863
|
+
// The panel is already a bordered surface: the field inside it must not be
|
|
1864
|
+
// a second bordered box (this used to render a TextInput).
|
|
1865
|
+
expect(row).not.toHaveClass('astryx-text-input');
|
|
1866
|
+
expect(search.closest('.astryx-text-input')).toBeNull();
|
|
1867
|
+
// The field is a rounded box inset from the panel edge, shaped like the
|
|
1868
|
+
// option rows under it — not a full-bleed header strip.
|
|
1869
|
+
expect(field).not.toBe(row);
|
|
1870
|
+
// ...and a divider separates it from the options.
|
|
1871
|
+
const separator = document.querySelector('[role="separator"]');
|
|
1872
|
+
if (!separator) {
|
|
1873
|
+
throw new Error('divider not found');
|
|
1874
|
+
}
|
|
1875
|
+
// Order: row, then divider, then the listbox.
|
|
1876
|
+
expect(
|
|
1877
|
+
row.compareDocumentPosition(separator) & Node.DOCUMENT_POSITION_FOLLOWING,
|
|
1878
|
+
).toBeTruthy();
|
|
1879
|
+
const listbox = screen.getByRole('listbox', h);
|
|
1880
|
+
expect(
|
|
1881
|
+
separator.compareDocumentPosition(listbox) &
|
|
1882
|
+
Node.DOCUMENT_POSITION_FOLLOWING,
|
|
1883
|
+
).toBeTruthy();
|
|
1884
|
+
});
|
|
1885
|
+
|
|
1886
|
+
it('keeps the search row outside the scrolling option list', async () => {
|
|
1887
|
+
const user = userEvent.setup();
|
|
1888
|
+
render(
|
|
1889
|
+
<MultiSelector
|
|
1890
|
+
label="Fruit"
|
|
1891
|
+
options={['Apple', 'Banana', 'Orange']}
|
|
1892
|
+
value={[]}
|
|
1893
|
+
onChange={() => {}}
|
|
1894
|
+
hasSearch
|
|
1895
|
+
/>,
|
|
1896
|
+
);
|
|
1897
|
+
await user.click(screen.getByRole('button', {name: 'Fruit'}));
|
|
1898
|
+
// The options scroll under the header rather than carrying it away, so the
|
|
1899
|
+
// field stays reachable in a long list.
|
|
1900
|
+
const search = screen.getByRole('combobox', h);
|
|
1901
|
+
const listbox = screen.getByRole('listbox', h);
|
|
1902
|
+
expect(listbox.contains(search)).toBe(false);
|
|
1903
|
+
});
|
|
1904
|
+
|
|
1778
1905
|
it('renders a decorative (aria-hidden) magnifier icon whenever hasSearch is on', async () => {
|
|
1779
1906
|
const user = userEvent.setup();
|
|
1780
1907
|
render(
|
|
@@ -1788,8 +1915,7 @@ describe('MultiSelector search affordances', () => {
|
|
|
1788
1915
|
);
|
|
1789
1916
|
await user.click(screen.getByRole('button', {name: 'Fruit'}));
|
|
1790
1917
|
const search = screen.getByRole('combobox', h);
|
|
1791
|
-
// The
|
|
1792
|
-
// sits inside the input container as a sibling of the <input>.
|
|
1918
|
+
// The magnifier leads the search row, as a sibling of the <input>.
|
|
1793
1919
|
const wrapper = search.parentElement;
|
|
1794
1920
|
const magnifier = wrapper?.querySelector('.astryx-icon');
|
|
1795
1921
|
expect(magnifier).toBeTruthy();
|
|
@@ -2088,7 +2214,7 @@ describe('MultiSelector dropdown option theme target', () => {
|
|
|
2088
2214
|
});
|
|
2089
2215
|
|
|
2090
2216
|
describe('MultiSelector popup theme target', () => {
|
|
2091
|
-
it('puts astryx-multi-selector-popup on the
|
|
2217
|
+
it('puts astryx-multi-selector-popup on the surface that paints, not the list inside it', async () => {
|
|
2092
2218
|
const user = userEvent.setup();
|
|
2093
2219
|
render(
|
|
2094
2220
|
<MultiSelector
|
|
@@ -2100,13 +2226,17 @@ describe('MultiSelector popup theme target', () => {
|
|
|
2100
2226
|
);
|
|
2101
2227
|
await user.click(screen.getByRole('combobox', {name: /Fruit/}));
|
|
2102
2228
|
|
|
2103
|
-
const layer = document.querySelector('[popover]') as HTMLElement;
|
|
2104
2229
|
const popup = document.querySelector(
|
|
2105
2230
|
'.astryx-multi-selector-popup',
|
|
2106
2231
|
) as HTMLElement;
|
|
2107
2232
|
expect(popup).not.toBeNull();
|
|
2233
|
+
expect(popup).toHaveClass('astryx-popover-surface');
|
|
2234
|
+
// The scrolling list is a descendant, not the target itself.
|
|
2235
|
+
expect(popup.querySelector('[role="listbox"]')).not.toBeNull();
|
|
2236
|
+
expect(popup.getAttribute('role')).toBeNull();
|
|
2237
|
+
|
|
2238
|
+
const layer = document.querySelector('[popover]') as HTMLElement;
|
|
2108
2239
|
expect(popup).not.toBe(layer);
|
|
2109
2240
|
expect(layer.contains(popup)).toBe(true);
|
|
2110
|
-
expect(popup.querySelector('[role="listbox"]')).not.toBeNull();
|
|
2111
2241
|
});
|
|
2112
2242
|
});
|