@astryxdesign/core 0.7.2-canary.55d8dff → 0.7.2-canary.577a62e
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/README.md +4 -4
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +8 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.js +4 -1
- package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
- package/dist/Chat/ChatLayoutScrollButton.js +1 -0
- package/dist/CheckboxList/CheckboxList.d.ts +1 -1
- package/dist/CheckboxList/CheckboxList.d.ts.map +1 -1
- package/dist/CheckboxList/CheckboxList.js +6 -2
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +2 -103
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +1 -1
- package/dist/DropdownMenu/DropdownMenuGroup.d.ts +6 -4
- package/dist/DropdownMenu/DropdownMenuGroup.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuGroup.js +4 -3
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +1 -1
- package/dist/DropdownMenu/menuRegionStops.d.ts +38 -0
- package/dist/DropdownMenu/menuRegionStops.d.ts.map +1 -0
- package/dist/DropdownMenu/menuRegionStops.js +131 -0
- package/dist/FormLayout/FormLayout.d.ts +5 -4
- package/dist/FormLayout/FormLayout.d.ts.map +1 -1
- package/dist/FormLayout/FormLayout.js +1 -1
- package/dist/FormLayout/FormLayoutContext.d.ts +3 -3
- package/dist/FormLayout/FormLayoutContext.js +3 -3
- package/dist/Grid/Grid.d.ts +8 -7
- package/dist/Grid/Grid.d.ts.map +1 -1
- package/dist/Grid/Grid.js +5 -4
- package/dist/Grid/GridSpan.d.ts +1 -1
- package/dist/Markdown/parser.d.ts.map +1 -1
- package/dist/Markdown/parser.js +8 -2
- package/dist/Markdown/plugins/headingLinks.d.ts.map +1 -1
- package/dist/Markdown/plugins/headingLinks.js +9 -1
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +5 -0
- package/dist/PowerSearch/PowerSearchValueEditor.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearchValueEditor.js +45 -11
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.js +6 -2
- package/dist/Token/Token.js +1 -1
- package/dist/TopNav/TopNavMegaMenu.js +2 -0
- package/dist/TopNav/TopNavMenu.d.ts.map +1 -1
- package/dist/TopNav/TopNavMenu.js +2 -0
- package/dist/astryx.css +1 -1
- package/dist/reset.a11y.chromium.spec.js +50 -1
- package/docs.mjs +4 -4
- package/package.json +3 -3
- package/scripts/agent-doc-state.mjs +29 -7
- package/scripts/postinstall.mjs +43 -2
- package/src/Avatar/Avatar.tsx +11 -2
- package/src/AvatarGroup/AvatarGroupOverflow.test.tsx +24 -1
- package/src/AvatarGroup/AvatarGroupOverflow.tsx +2 -0
- package/src/Chat/ChatLayoutScrollButton.test.tsx +13 -0
- package/src/Chat/ChatLayoutScrollButton.tsx +4 -2
- package/src/Chat/__tests__/ChatLayoutScrollButton.a11y.chromium.spec.ts +64 -19
- package/src/CheckboxList/CheckboxList.fr10.test.tsx +29 -2
- package/src/CheckboxList/CheckboxList.tsx +4 -1
- package/src/ContextMenu/ContextMenu.test.tsx +12 -2
- package/src/DropdownMenu/DropdownMenu.test.tsx +3 -4
- package/src/DropdownMenu/DropdownMenu.tsx +7 -134
- package/src/DropdownMenu/DropdownMenuCheckboxItem.doc.mjs +1 -1
- package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +1 -1
- package/src/DropdownMenu/DropdownMenuGroup.doc.mjs +169 -10
- package/src/DropdownMenu/DropdownMenuGroup.tsx +6 -4
- package/src/DropdownMenu/DropdownMenuRadioItem.doc.mjs +1 -1
- package/src/DropdownMenu/DropdownMenuRadioItem.tsx +1 -1
- package/src/DropdownMenu/__tests__/DropdownMenuGroupAudit.a11y.chromium.spec.ts +334 -0
- package/src/DropdownMenu/menuRegionStops.ts +167 -0
- package/src/Field/FieldLabel.doc.mjs +133 -26
- package/src/Field/FieldLabel.spec.md +172 -0
- package/src/Field/__tests__/FieldLabelAudit.a11y.chromium.spec.ts +296 -0
- package/src/FormLayout/FormLayout.doc.mjs +6 -3
- package/src/FormLayout/FormLayout.spec.md +192 -0
- package/src/FormLayout/FormLayout.test.tsx +15 -0
- package/src/FormLayout/FormLayout.tsx +7 -6
- package/src/FormLayout/FormLayoutContext.ts +3 -3
- package/src/Grid/Grid.doc.mjs +7 -1
- package/src/Grid/Grid.spec.md +93 -113
- package/src/Grid/Grid.test.tsx +22 -0
- package/src/Grid/Grid.tsx +10 -9
- package/src/Grid/GridSpan.doc.mjs +3 -3
- package/src/Grid/GridSpan.tsx +1 -1
- package/src/Indicator/Indicator.doc.mjs +3 -3
- package/src/Markdown/parser.ts +8 -2
- package/src/Markdown/plugins/headingLinks.ts +9 -1
- package/src/PowerSearch/PowerSearch.test.tsx +62 -0
- package/src/PowerSearch/PowerSearch.tsx +9 -0
- package/src/PowerSearch/PowerSearchValueEditor.test.tsx +71 -0
- package/src/PowerSearch/PowerSearchValueEditor.tsx +45 -0
- package/src/Resizable/useResizable.doc.mjs +0 -1
- package/src/SideNav/SideNav.spec.md +227 -0
- package/src/SideNav/SideNav.test.tsx +41 -0
- package/src/SideNav/SideNavItem.tsx +5 -2
- package/src/Table/Table.spec.md +6 -1
- package/src/Table/plugins/activation/useTableActivation.spec.md +254 -0
- package/src/Token/Token.tsx +1 -1
- package/src/Token/__tests__/TokenRemoveTarget.a11y.chromium.spec.ts +86 -0
- package/src/Toolbar/Toolbar.test.tsx +0 -8
- package/src/Toolbar/__tests__/Toolbar.a11y.chromium.spec.ts +163 -0
- package/src/Toolbar/__tests__/Toolbar.a11y.renders.tsx +63 -0
- package/src/Toolbar/__tests__/Toolbar.a11y.states.ts +142 -0
- package/src/Toolbar/__tests__/Toolbar.a11y.test.tsx +126 -0
- package/src/TopNav/TopNavMegaMenu.test.tsx +25 -0
- package/src/TopNav/TopNavMegaMenu.tsx +2 -0
- package/src/TopNav/TopNavMenu.test.tsx +27 -1
- package/src/TopNav/TopNavMenu.tsx +2 -0
- package/src/postinstall.test.mjs +33 -0
- package/src/reset.a11y.chromium.spec.ts +52 -1
- package/src/reset.css +9 -0
- package/src/reset.test.ts +11 -0
- package/src/unicodePropertyEscapes.source-build.test.mjs +69 -0
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* @file reset.a11y.chromium.spec.ts
|
|
5
5
|
* @input packages/core/src/reset.css, alone on a blank Chromium page
|
|
6
6
|
* @output Browser evidence that the reset reaches ::before, ::after,
|
|
7
|
-
* ::placeholder
|
|
7
|
+
* ::placeholder, the search field's cancel button and the dropdown marker
|
|
8
|
+
* of a field with a list
|
|
8
9
|
* @position reset.css integration proof. A pseudo-element inside :where() is
|
|
9
10
|
* invalid, and the forgiving list drops it without an error, so only a
|
|
10
11
|
* shipping engine shows which of the reset's rules still match.
|
|
@@ -13,6 +14,8 @@
|
|
|
13
14
|
import * as fs from 'node:fs';
|
|
14
15
|
import {fileURLToPath} from 'node:url';
|
|
15
16
|
import {expect, test, type Page} from '@playwright/test';
|
|
17
|
+
// @ts-expect-error -- pngjs ships no declarations; runtime support is pinned.
|
|
18
|
+
import {PNG} from 'pngjs';
|
|
16
19
|
|
|
17
20
|
const RESET_CSS = fs.readFileSync(
|
|
18
21
|
fileURLToPath(new URL('./reset.css', import.meta.url)),
|
|
@@ -154,6 +157,54 @@ test('a search field has no cancel button to clear it', async ({page}) => {
|
|
|
154
157
|
await expect(query).toHaveValue('query');
|
|
155
158
|
});
|
|
156
159
|
|
|
160
|
+
test('the dropdown marker of a field with a list sits in its middle', async ({
|
|
161
|
+
page,
|
|
162
|
+
}) => {
|
|
163
|
+
// Chromium shows the marker while the pointer is over the field and draws
|
|
164
|
+
// it as a glyph set on the field's line-height, the 1.5 the reset inherits.
|
|
165
|
+
await page.evaluate(() => {
|
|
166
|
+
const field = document.createElement('input');
|
|
167
|
+
field.id = 'fruit';
|
|
168
|
+
field.setAttribute('list', 'fruits');
|
|
169
|
+
const options = document.createElement('datalist');
|
|
170
|
+
options.id = 'fruits';
|
|
171
|
+
options.append(new Option('Apple'));
|
|
172
|
+
document.body.append(field, options);
|
|
173
|
+
});
|
|
174
|
+
await page.addStyleTag({
|
|
175
|
+
content: `
|
|
176
|
+
#fruit {
|
|
177
|
+
display: block;
|
|
178
|
+
width: 240px;
|
|
179
|
+
padding: 7px 11px;
|
|
180
|
+
border: 1px solid;
|
|
181
|
+
line-height: 1.5;
|
|
182
|
+
}
|
|
183
|
+
`,
|
|
184
|
+
});
|
|
185
|
+
const field = page.locator('#fruit');
|
|
186
|
+
await field.hover();
|
|
187
|
+
const png = PNG.sync.read(await field.screenshot({caret: 'hide'}));
|
|
188
|
+
// The marker is the only ink in the field's last 32px, inside the border.
|
|
189
|
+
const inkRows: number[] = [];
|
|
190
|
+
for (let y = 2; y < png.height - 2; y++) {
|
|
191
|
+
for (let x = png.width - 32; x < png.width - 2; x++) {
|
|
192
|
+
const offset = (y * png.width + x) * 4;
|
|
193
|
+
const luminance =
|
|
194
|
+
(png.data[offset] + png.data[offset + 1] + png.data[offset + 2]) / 3;
|
|
195
|
+
if (luminance < 160) {
|
|
196
|
+
inkRows.push(y);
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
expect(inkRows.length, 'the hovered field shows its marker').toBeGreaterThan(
|
|
202
|
+
0,
|
|
203
|
+
);
|
|
204
|
+
const markerMiddle = (inkRows[0] + inkRows[inkRows.length - 1] + 1) / 2;
|
|
205
|
+
expect(Math.abs(markerMiddle - png.height / 2)).toBeLessThanOrEqual(1);
|
|
206
|
+
});
|
|
207
|
+
|
|
157
208
|
test('a later layer and an unlayered rule still override the reset', async ({
|
|
158
209
|
page,
|
|
159
210
|
}) => {
|
package/src/reset.css
CHANGED
|
@@ -265,6 +265,15 @@
|
|
|
265
265
|
color: var(--color-text-secondary, light-dark(#4E606F, #AAAFB5));
|
|
266
266
|
}
|
|
267
267
|
|
|
268
|
+
/*
|
|
269
|
+
* Chromium draws the dropdown marker of a field with a `list` as a glyph
|
|
270
|
+
* set on the line-height the field inherits, so with any line-height above
|
|
271
|
+
* 1 the marker sits below the middle of the field.
|
|
272
|
+
*/
|
|
273
|
+
:where([list])::-webkit-calendar-picker-indicator {
|
|
274
|
+
line-height: 1;
|
|
275
|
+
}
|
|
276
|
+
|
|
268
277
|
/*
|
|
269
278
|
* Remove the inner padding and cancel buttons in search inputs
|
|
270
279
|
* for Chrome and Safari.
|
package/src/reset.test.ts
CHANGED
|
@@ -172,6 +172,17 @@ describe('reset.css pseudo-element rules', () => {
|
|
|
172
172
|
).toBe(true);
|
|
173
173
|
});
|
|
174
174
|
|
|
175
|
+
it('sets the dropdown marker of a field with a list on a single line', () => {
|
|
176
|
+
const marker = ruleBlocks.filter(({selector}) =>
|
|
177
|
+
topLevelSelectors(selector).some(entry =>
|
|
178
|
+
entry.endsWith('::-webkit-calendar-picker-indicator'),
|
|
179
|
+
),
|
|
180
|
+
);
|
|
181
|
+
expect(marker.map(({body}) => body).join('\n')).toMatch(
|
|
182
|
+
/line-height:\s*1;/,
|
|
183
|
+
);
|
|
184
|
+
});
|
|
185
|
+
|
|
175
186
|
it('falls back to the --color-text-secondary default for placeholders', () => {
|
|
176
187
|
// A page that loads only the reset never defines the token. The fallback
|
|
177
188
|
// repeats the token's own default, so that page shows the placeholder an
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file unicodePropertyEscapes.source-build.test.mjs
|
|
5
|
+
* @input Every Core source module a bundler compiles (tests and stories
|
|
6
|
+
* excluded)
|
|
7
|
+
* @output Fails when a module writes a Unicode property escape (`\p{…}` or
|
|
8
|
+
* `\P{…}`) as a regular expression literal
|
|
9
|
+
* @position Guard for source builds: for targets without property escapes, or
|
|
10
|
+
* with no browserslist, Babel rewrites such a literal, and the copy bundled
|
|
11
|
+
* with Next.js lacks the Unicode data to do it, so the build fails with
|
|
12
|
+
* "Unknown property". Modules build these expressions at runtime instead,
|
|
13
|
+
* as NumberInput/numberParser.ts does.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import fs from 'node:fs';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
import {fileURLToPath} from 'node:url';
|
|
19
|
+
import {parseSync} from '@babel/core';
|
|
20
|
+
import {describe, expect, it} from 'vitest';
|
|
21
|
+
|
|
22
|
+
const SRC = path.dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
const SKIPPED = /\.(test|stories)\.tsx?$|\.d\.ts$/;
|
|
24
|
+
|
|
25
|
+
function sourceFiles(dir) {
|
|
26
|
+
return fs.readdirSync(dir, {withFileTypes: true}).flatMap(entry => {
|
|
27
|
+
const file = path.join(dir, entry.name);
|
|
28
|
+
if (entry.isDirectory()) {
|
|
29
|
+
return entry.name === '__tests__' ? [] : sourceFiles(file);
|
|
30
|
+
}
|
|
31
|
+
return /\.tsx?$/.test(entry.name) && !SKIPPED.test(entry.name)
|
|
32
|
+
? [file]
|
|
33
|
+
: [];
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Every regular expression literal in a parsed tree. */
|
|
38
|
+
function regexLiterals(node, found = []) {
|
|
39
|
+
if (node == null || typeof node !== 'object') return found;
|
|
40
|
+
if (node.type === 'RegExpLiteral') found.push(node);
|
|
41
|
+
for (const [key, value] of Object.entries(node)) {
|
|
42
|
+
if (key !== 'loc' && key !== 'extra') regexLiterals(value, found);
|
|
43
|
+
}
|
|
44
|
+
return found;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
describe('Core regular expressions in source builds', () => {
|
|
48
|
+
it('writes no Unicode property escape as a regex literal', () => {
|
|
49
|
+
const offenders = [];
|
|
50
|
+
for (const file of sourceFiles(SRC)) {
|
|
51
|
+
const source = fs.readFileSync(file, 'utf8');
|
|
52
|
+
if (!/\\[pP]\{/.test(source)) continue;
|
|
53
|
+
const ast = parseSync(source, {
|
|
54
|
+
filename: file,
|
|
55
|
+
babelrc: false,
|
|
56
|
+
configFile: false,
|
|
57
|
+
parserOpts: {plugins: ['typescript', 'jsx']},
|
|
58
|
+
});
|
|
59
|
+
for (const literal of regexLiterals(ast)) {
|
|
60
|
+
if (/\\[pP]\{/.test(literal.pattern)) {
|
|
61
|
+
offenders.push(
|
|
62
|
+
`${path.relative(SRC, file)}:${literal.loc.start.line} /${literal.pattern}/${literal.flags}`,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
expect(offenders).toEqual([]);
|
|
68
|
+
});
|
|
69
|
+
});
|