@astryxdesign/core 0.1.6-canary.fd658d7 → 0.1.6
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/dist/Link/Link.d.ts +0 -4
- package/dist/Link/Link.d.ts.map +1 -1
- package/dist/Link/Link.js +0 -28
- package/dist/TopNav/TopNav.d.ts +0 -1
- package/dist/TopNav/TopNav.d.ts.map +1 -1
- package/dist/TopNav/TopNav.js +1 -1
- package/dist/astryx.umd.js +31 -31
- package/dist/astryx.umd.js.map +3 -3
- package/package.json +1 -1
- package/src/Link/Link.test.tsx +2 -74
- package/src/Link/Link.tsx +0 -44
- package/src/TopNav/TopNav.doc.mjs +0 -1
- package/src/TopNav/TopNav.test.tsx +0 -30
- package/src/TopNav/TopNav.tsx +4 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/core",
|
|
3
|
-
"version": "0.1.6
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"displayName": "Astryx Core",
|
|
5
5
|
"description": "The component library. Accessible, themeable React components with built-in spacing, dark mode, and StyleX styling.",
|
|
6
6
|
"author": "Meta Open Source",
|
package/src/Link/Link.test.tsx
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import {describe, it, expect, vi} from 'vitest';
|
|
13
|
-
import {
|
|
13
|
+
import {render, screen} from '@testing-library/react';
|
|
14
14
|
import userEvent from '@testing-library/user-event';
|
|
15
15
|
import {Link} from './Link';
|
|
16
16
|
import {LinkProvider} from './LinkProvider';
|
|
@@ -153,81 +153,9 @@ describe('Link', () => {
|
|
|
153
153
|
Disabled Link
|
|
154
154
|
</Link>,
|
|
155
155
|
);
|
|
156
|
-
|
|
157
|
-
const link = screen.getByText('Disabled Link').closest('a');
|
|
158
|
-
expect(link).not.toBeNull();
|
|
156
|
+
const link = screen.getByRole('link');
|
|
159
157
|
expect(link).toHaveAttribute('aria-disabled', 'true');
|
|
160
158
|
expect(link).toHaveAttribute('tabIndex', '-1');
|
|
161
|
-
// Visual classes are preserved in the disabled state.
|
|
162
|
-
expect(link?.className).toContain('astryx-link');
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it('disabled link has no href attribute', () => {
|
|
166
|
-
render(
|
|
167
|
-
<Link href="/test" isDisabled>
|
|
168
|
-
Disabled Link
|
|
169
|
-
</Link>,
|
|
170
|
-
);
|
|
171
|
-
const link = screen.getByText('Disabled Link').closest('a');
|
|
172
|
-
expect(link).not.toBeNull();
|
|
173
|
-
expect(link).not.toHaveAttribute('href');
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it('clicking a disabled link cancels default navigation', () => {
|
|
177
|
-
render(
|
|
178
|
-
<Link href="/test" isDisabled>
|
|
179
|
-
Disabled Link
|
|
180
|
-
</Link>,
|
|
181
|
-
);
|
|
182
|
-
const link = screen.getByText('Disabled Link').closest('a');
|
|
183
|
-
expect(link).not.toBeNull();
|
|
184
|
-
// fireEvent bypasses pointer-events, simulating programmatic/AT
|
|
185
|
-
// activation. It returns false when preventDefault was called, i.e.
|
|
186
|
-
// any default navigation behavior is cancelled.
|
|
187
|
-
const notCancelled = fireEvent.click(link as HTMLAnchorElement);
|
|
188
|
-
expect(notCancelled).toBe(false);
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it('disabled link with onClick fires neither navigation nor the consumer onClick', () => {
|
|
192
|
-
const handleClick = vi.fn();
|
|
193
|
-
const {container} = render(
|
|
194
|
-
<Link href="/test" isDisabled onClick={handleClick}>
|
|
195
|
-
Disabled Link
|
|
196
|
-
</Link>,
|
|
197
|
-
);
|
|
198
|
-
// With onClick present, a disabled Link renders as a disabled <button>
|
|
199
|
-
// (useInteractiveRole excludes a disabled href). Either way the rendered
|
|
200
|
-
// root must carry no live href and never invoke the consumer handler.
|
|
201
|
-
const el = container.firstElementChild as HTMLElement;
|
|
202
|
-
expect(el).not.toHaveAttribute('href');
|
|
203
|
-
fireEvent.click(el);
|
|
204
|
-
expect(handleClick).not.toHaveBeenCalled();
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it('disabled link omits target and rel', () => {
|
|
208
|
-
render(
|
|
209
|
-
<Link href="https://example.com" isDisabled isExternalLink>
|
|
210
|
-
Disabled External
|
|
211
|
-
</Link>,
|
|
212
|
-
);
|
|
213
|
-
const link = screen.getByText('Disabled External').closest('a');
|
|
214
|
-
expect(link).not.toBeNull();
|
|
215
|
-
expect(link).not.toHaveAttribute('target');
|
|
216
|
-
expect(link).not.toHaveAttribute('rel');
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
it('disabled link renders a plain anchor, not the custom LinkComponent', () => {
|
|
220
|
-
render(
|
|
221
|
-
<LinkProvider component={CustomLink}>
|
|
222
|
-
<Link href="/custom" as={CustomLink} isDisabled>
|
|
223
|
-
Disabled Custom
|
|
224
|
-
</Link>
|
|
225
|
-
</LinkProvider>,
|
|
226
|
-
);
|
|
227
|
-
const link = screen.getByText('Disabled Custom').closest('a');
|
|
228
|
-
expect(link).not.toBeNull();
|
|
229
|
-
expect(link).not.toHaveAttribute('data-custom-link');
|
|
230
|
-
expect(link).not.toHaveAttribute('href');
|
|
231
159
|
});
|
|
232
160
|
|
|
233
161
|
it('renders external link with icon and target="_blank"', () => {
|
package/src/Link/Link.tsx
CHANGED
|
@@ -174,10 +174,6 @@ export interface LinkProps extends BaseProps<
|
|
|
174
174
|
hasUnderline?: boolean;
|
|
175
175
|
/**
|
|
176
176
|
* Whether the link is disabled.
|
|
177
|
-
* A disabled link renders as a plain anchor without an href (and without
|
|
178
|
-
* target/rel/onClick), so it cannot be focused or activated — no
|
|
179
|
-
* navigation and no onClick, even via programmatic focus or assistive
|
|
180
|
-
* technology activation.
|
|
181
177
|
* @default false
|
|
182
178
|
*/
|
|
183
179
|
isDisabled?: boolean;
|
|
@@ -265,15 +261,6 @@ export interface LinkProps extends BaseProps<
|
|
|
265
261
|
children: ReactNode;
|
|
266
262
|
}
|
|
267
263
|
|
|
268
|
-
/**
|
|
269
|
-
* Click handler for disabled links. The disabled anchor renders without an
|
|
270
|
-
* href, so there is no navigation to block in practice; preventDefault is a
|
|
271
|
-
* defensive guard against synthetic/programmatic clicks.
|
|
272
|
-
*/
|
|
273
|
-
function preventDefaultClick(event: React.MouseEvent<HTMLAnchorElement>): void {
|
|
274
|
-
event.preventDefault();
|
|
275
|
-
}
|
|
276
|
-
|
|
277
264
|
/**
|
|
278
265
|
* A styled anchor link component.
|
|
279
266
|
*
|
|
@@ -381,37 +368,6 @@ export function Link({
|
|
|
381
368
|
{sharedContent}
|
|
382
369
|
</button>
|
|
383
370
|
);
|
|
384
|
-
} else if (isDisabled) {
|
|
385
|
-
// A disabled link renders as a plain <a> with no href: an href-less
|
|
386
|
-
// anchor is not focusable and exposes no link affordance, so programmatic
|
|
387
|
-
// focus + Enter, AT activation commands, and middle-click cannot navigate
|
|
388
|
-
// or fire the consumer onClick. The router LinkComponent is deliberately
|
|
389
|
-
// skipped — a disabled link performs no navigation, and custom router
|
|
390
|
-
// links may require a live href. target/rel are omitted with the href.
|
|
391
|
-
linkElement = (
|
|
392
|
-
<a
|
|
393
|
-
ref={ref as React.Ref<HTMLAnchorElement>}
|
|
394
|
-
onClick={preventDefaultClick}
|
|
395
|
-
aria-label={label || undefined}
|
|
396
|
-
aria-disabled={true}
|
|
397
|
-
tabIndex={-1}
|
|
398
|
-
{...mergeProps(
|
|
399
|
-
themeProps('link', {color}),
|
|
400
|
-
stylex.props(
|
|
401
|
-
styles.base,
|
|
402
|
-
linkColorStyles[color],
|
|
403
|
-
hasUnderline && styles.hasUnderline,
|
|
404
|
-
isStandalone && styles.standalone,
|
|
405
|
-
styles.disabled,
|
|
406
|
-
xstyle,
|
|
407
|
-
),
|
|
408
|
-
className,
|
|
409
|
-
style,
|
|
410
|
-
)}
|
|
411
|
-
{...props}>
|
|
412
|
-
{sharedContent}
|
|
413
|
-
</a>
|
|
414
|
-
);
|
|
415
371
|
} else {
|
|
416
372
|
linkElement = (
|
|
417
373
|
<LinkComponent
|
|
@@ -13,7 +13,6 @@ 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
15
|
import {TopNav} from './TopNav';
|
|
16
|
-
import {TopNavRenderContext} from './TopNavRenderContext';
|
|
17
16
|
import {TopNavHeading} from './TopNavHeading';
|
|
18
17
|
import {NavIcon} from '../NavIcon';
|
|
19
18
|
import {TopNavItem} from './TopNavItem';
|
|
@@ -46,35 +45,6 @@ describe('TopNav', () => {
|
|
|
46
45
|
);
|
|
47
46
|
});
|
|
48
47
|
|
|
49
|
-
it('defaults the landmark label to "Top navigation" when label is omitted', () => {
|
|
50
|
-
render(<TopNav />);
|
|
51
|
-
expect(
|
|
52
|
-
screen.getByRole('navigation', {name: 'Top navigation'}),
|
|
53
|
-
).toBeInTheDocument();
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('defaults the landmark label in mobile-bar mode', () => {
|
|
57
|
-
render(
|
|
58
|
-
<TopNavRenderContext value="mobile-bar">
|
|
59
|
-
<TopNav heading={<span>Logo</span>} />
|
|
60
|
-
</TopNavRenderContext>,
|
|
61
|
-
);
|
|
62
|
-
expect(
|
|
63
|
-
screen.getByRole('navigation', {name: 'Top navigation'}),
|
|
64
|
-
).toBeInTheDocument();
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('custom label overrides the default in mobile-bar mode', () => {
|
|
68
|
-
render(
|
|
69
|
-
<TopNavRenderContext value="mobile-bar">
|
|
70
|
-
<TopNav label="Utility navigation" />
|
|
71
|
-
</TopNavRenderContext>,
|
|
72
|
-
);
|
|
73
|
-
expect(
|
|
74
|
-
screen.getByRole('navigation', {name: 'Utility navigation'}),
|
|
75
|
-
).toBeInTheDocument();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
48
|
it('renders heading slot content', () => {
|
|
79
49
|
render(<TopNav heading={<span data-testid="title-content">Logo</span>} />);
|
|
80
50
|
expect(screen.getByTestId('title-content')).toBeInTheDocument();
|
package/src/TopNav/TopNav.tsx
CHANGED
|
@@ -147,7 +147,6 @@ export interface TopNavProps extends BaseProps<HTMLElement> {
|
|
|
147
147
|
/**
|
|
148
148
|
* Accessible label for the navigation landmark.
|
|
149
149
|
* Helps screen readers identify the navigation area.
|
|
150
|
-
* @default 'Top navigation'
|
|
151
150
|
*/
|
|
152
151
|
label?: string;
|
|
153
152
|
}
|
|
@@ -177,7 +176,7 @@ export function TopNav({
|
|
|
177
176
|
children,
|
|
178
177
|
centerContent,
|
|
179
178
|
endContent,
|
|
180
|
-
label
|
|
179
|
+
label,
|
|
181
180
|
xstyle,
|
|
182
181
|
className,
|
|
183
182
|
style,
|
|
@@ -295,7 +294,9 @@ export function TopNav({
|
|
|
295
294
|
) : (
|
|
296
295
|
endContent && (
|
|
297
296
|
<div {...stylex.props(styles.endContent)}>
|
|
298
|
-
<TopNavSlotContext value="end">
|
|
297
|
+
<TopNavSlotContext value="end">
|
|
298
|
+
{endContent}
|
|
299
|
+
</TopNavSlotContext>
|
|
299
300
|
</div>
|
|
300
301
|
)
|
|
301
302
|
)}
|