@astryxdesign/core 0.1.4-canary.37c19d3 → 0.1.4-canary.39ffd21
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/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +1 -8
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +11 -31
- package/dist/Calendar/styles.d.ts +2 -2
- package/dist/Calendar/styles.d.ts.map +1 -1
- package/dist/Calendar/styles.js +2 -2
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +0 -1
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +1 -8
- package/dist/CheckboxInput/CheckboxInput.d.ts +1 -1
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +1 -3
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +3 -26
- package/dist/CodeBlock/CodeBlock.d.ts +1 -10
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +5 -23
- package/dist/Collapsible/Collapsible.d.ts +3 -9
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +13 -101
- package/dist/Collapsible/CollapsibleGroup.d.ts +7 -39
- package/dist/Collapsible/CollapsibleGroup.d.ts.map +1 -1
- package/dist/Collapsible/CollapsibleGroup.js +9 -70
- package/dist/Collapsible/CollapsibleGroupContext.d.ts +0 -31
- package/dist/Collapsible/CollapsibleGroupContext.d.ts.map +1 -1
- package/dist/Collapsible/CollapsibleGroupContext.js +3 -33
- package/dist/Collapsible/index.d.ts +0 -1
- package/dist/Collapsible/index.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +0 -2
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +0 -2
- package/dist/Grid/Grid.d.ts +3 -4
- package/dist/Grid/Grid.d.ts.map +1 -1
- package/dist/Grid/Grid.js +9 -26
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Icon/Icon.js +1 -5
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +2 -26
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +16 -32
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +36 -87
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +21 -39
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +1 -2
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +2 -10
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +2 -6
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +7 -34
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +2 -12
- package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
- package/dist/Table/plugins/columnResize/useTableColumnResize.js +2 -5
- package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/paginateData.js +1 -5
- package/dist/Table/plugins/selection/useTableSelectionState.d.ts.map +1 -1
- package/dist/Table/plugins/selection/useTableSelectionState.js +1 -11
- package/dist/Table/plugins/sortable/useTableSortableState.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortableState.js +4 -8
- package/dist/Thumbnail/Thumbnail.d.ts +2 -3
- package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
- package/dist/Thumbnail/Thumbnail.js +0 -1
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +1 -6
- package/dist/Toast/Toast.d.ts.map +1 -1
- package/dist/Toast/Toast.js +3 -11
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +0 -12
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +2 -5
- package/dist/astryx.css +0 -7
- package/dist/astryx.umd.js +48 -48
- package/dist/astryx.umd.js.map +4 -4
- package/dist/hooks/focusableSelector.d.ts +1 -1
- package/dist/hooks/focusableSelector.d.ts.map +1 -1
- package/dist/hooks/focusableSelector.js +1 -1
- package/dist/hooks/useFocusTrap.d.ts +0 -3
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +1 -38
- package/dist/theme/defineTheme.d.ts +1 -1
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/expandColorScale.d.ts.map +1 -1
- package/dist/theme/expandColorScale.js +3 -10
- package/dist/theme/hct.d.ts +15 -0
- package/dist/theme/hct.d.ts.map +1 -1
- package/dist/theme/hct.js +9 -9
- package/dist/theme/tokens.d.ts +2 -10
- package/dist/theme/tokens.d.ts.map +1 -1
- package/dist/theme/tokens.js +4 -220
- package/dist/utils/index.d.ts +0 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -2
- package/dist/utils/timeParser.d.ts.map +1 -1
- package/dist/utils/timeParser.js +5 -8
- package/package.json +1 -1
- package/src/Avatar/Avatar.doc.mjs +0 -11
- package/src/Banner/Banner.doc.mjs +0 -8
- package/src/Banner/Banner.test.tsx +0 -38
- package/src/Banner/Banner.tsx +1 -8
- package/src/Calendar/Calendar.doc.mjs +1 -1
- package/src/Calendar/Calendar.test.tsx +4 -112
- package/src/Calendar/Calendar.tsx +5 -26
- package/src/Calendar/styles.ts +2 -12
- package/src/Carousel/Carousel.doc.mjs +12 -12
- package/src/Carousel/Carousel.test.tsx +0 -13
- package/src/Carousel/Carousel.tsx +0 -1
- package/src/Chat/Chat.doc.mjs +1 -1
- package/src/Chat/ChatToolCalls.test.tsx +0 -55
- package/src/Chat/ChatToolCalls.tsx +12 -10
- package/src/CheckboxInput/CheckboxInput.doc.mjs +3 -3
- package/src/CheckboxInput/CheckboxInput.test.tsx +4 -71
- package/src/CheckboxInput/CheckboxInput.tsx +0 -2
- package/src/Citation/Citation.doc.mjs +0 -5
- package/src/ClickableCard/ClickableCard.tsx +2 -45
- package/src/CodeBlock/CodeBlock.doc.mjs +2 -7
- package/src/CodeBlock/CodeBlock.test.tsx +3 -87
- package/src/CodeBlock/CodeBlock.tsx +3 -43
- package/src/Collapsible/Collapsible.doc.mjs +4 -8
- package/src/Collapsible/Collapsible.tsx +10 -86
- package/src/Collapsible/CollapsibleGroup.doc.mjs +3 -27
- package/src/Collapsible/CollapsibleGroup.test.tsx +0 -288
- package/src/Collapsible/CollapsibleGroup.tsx +9 -107
- package/src/Collapsible/CollapsibleGroupContext.tsx +2 -42
- package/src/Collapsible/index.ts +0 -4
- package/src/CommandPalette/CommandPaletteEmpty.doc.mjs +0 -5
- package/src/CommandPalette/CommandPaletteGroup.doc.mjs +0 -10
- package/src/CommandPalette/CommandPaletteInput.doc.mjs +0 -6
- package/src/CommandPalette/CommandPaletteItem.doc.mjs +0 -6
- package/src/CommandPalette/CommandPaletteList.doc.mjs +0 -9
- package/src/ContextMenu/ContextMenu.doc.mjs +0 -15
- package/src/ContextMenu/ContextMenu.test.tsx +0 -15
- package/src/ContextMenu/ContextMenu.tsx +0 -2
- package/src/ContextMenu/ContextMenuItem.doc.mjs +0 -6
- package/src/DateTimeInput/DateTimeInput.test.tsx +0 -70
- package/src/DateTimeInput/DateTimeInput.tsx +0 -2
- package/src/Dialog/DialogHeader.doc.mjs +0 -45
- package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -1
- package/src/Field/FieldLabel.test.tsx +2 -2
- package/src/FormLayout/__snapshots__/FormLayout.test.tsx.snap +3 -0
- package/src/Grid/Grid.test.tsx +18 -43
- package/src/Grid/Grid.tsx +9 -26
- package/src/Icon/Icon.test.tsx +29 -41
- package/src/Icon/Icon.tsx +2 -6
- package/src/Layout/LayoutContent.doc.mjs +0 -12
- package/src/Layout/LayoutPanel.doc.mjs +0 -40
- package/src/Lightbox/Lightbox.doc.mjs +0 -5
- package/src/Lightbox/Lightbox.test.tsx +2 -123
- package/src/Lightbox/Lightbox.tsx +1 -29
- package/src/NavMenu/NavMenu.doc.mjs +0 -15
- package/src/NumberInput/NumberInput.test.tsx +3 -48
- package/src/NumberInput/NumberInput.tsx +15 -32
- package/src/Overlay/Overlay.doc.mjs +0 -6
- package/src/Pagination/Pagination.test.tsx +0 -143
- package/src/Pagination/Pagination.tsx +23 -75
- package/src/Popover/Popover.test.tsx +0 -65
- package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
- package/src/PowerSearch/PowerSearch.test.tsx +1 -109
- package/src/PowerSearch/PowerSearch.tsx +16 -37
- package/src/ProgressBar/ProgressBar.test.tsx +0 -10
- package/src/ProgressBar/ProgressBar.tsx +1 -2
- package/src/Resizable/ResizeHandle.tsx +2 -10
- package/src/SegmentedControl/SegmentedControl.doc.mjs +1 -1
- package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
- package/src/SideNav/SideNav.doc.mjs +1 -7
- package/src/SideNav/SideNav.test.tsx +0 -20
- package/src/SideNav/SideNav.tsx +2 -6
- package/src/TabList/TabList.test.tsx +1 -185
- package/src/TabList/TabMenu.tsx +9 -45
- package/src/Table/BaseTable.tsx +1 -11
- package/src/Table/Table.test.tsx +0 -51
- package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +0 -13
- package/src/Table/plugins/columnResize/useTableColumnResize.tsx +1 -3
- package/src/Table/plugins/pagination/paginateData.ts +1 -5
- package/src/Table/plugins/pagination/useTablePagination.test.tsx +0 -13
- package/src/Table/plugins/selection/useTableSelectionState.test.tsx +0 -23
- package/src/Table/plugins/selection/useTableSelectionState.tsx +6 -11
- package/src/Table/plugins/sortable/useTableSortableState.test.tsx +1 -35
- package/src/Table/plugins/sortable/useTableSortableState.tsx +4 -8
- package/src/TextArea/TextArea.test.tsx +3 -3
- package/src/TextInput/TextInput.test.tsx +3 -3
- package/src/Thumbnail/Thumbnail.test.tsx +2 -36
- package/src/Thumbnail/Thumbnail.tsx +2 -4
- package/src/TimeInput/TimeInput.test.tsx +0 -37
- package/src/TimeInput/TimeInput.tsx +1 -11
- package/src/Timestamp/Timestamp.doc.mjs +1 -1
- package/src/Toast/Toast.tsx +3 -11
- package/src/Toast/ToastViewport.test.tsx +0 -64
- package/src/Toast/ToastViewport.tsx +0 -12
- package/src/ToggleButton/ToggleButton.doc.mjs +0 -1
- package/src/Tokenizer/Tokenizer.doc.mjs +2 -2
- package/src/TopNav/TopNav.doc.mjs +1 -1
- package/src/TreeList/TreeList.doc.mjs +2 -2
- package/src/Typeahead/BaseTypeahead.doc.mjs +0 -19
- package/src/Typeahead/BaseTypeahead.tsx +2 -5
- package/src/Typeahead/Typeahead.doc.mjs +1 -1
- package/src/Typeahead/Typeahead.test.tsx +1 -45
- package/src/hooks/focusableSelector.ts +1 -1
- package/src/hooks/useFocusTrap.doc.mjs +2 -4
- package/src/hooks/useFocusTrap.test.tsx +1 -149
- package/src/hooks/useFocusTrap.ts +1 -50
- package/src/theme/SyntaxTheme.doc.mjs +4 -4
- package/src/theme/defineTheme.ts +13 -4
- package/src/theme/derivedVarRegistry.test.ts +2 -2
- package/src/theme/expandColorScale.test.ts +0 -29
- package/src/theme/expandColorScale.ts +6 -11
- package/src/theme/hct.ts +17 -9
- package/src/theme/tokens.test.ts +9 -182
- package/src/theme/tokens.ts +4 -239
- package/src/theme/useTheme.test.tsx +0 -20
- package/src/utils/index.ts +0 -3
- package/src/utils/timeParser.test.ts +0 -13
- package/src/utils/timeParser.ts +5 -8
- package/dist/utils/color.d.ts +0 -42
- package/dist/utils/color.d.ts.map +0 -1
- package/dist/utils/color.js +0 -150
- package/src/Resizable/ResizeHandle.test.tsx +0 -180
- package/src/__tests__/TestIcon.tsx +0 -26
- package/src/docPropReferences.test.ts +0 -178
- package/src/utils/color.test.ts +0 -133
- package/src/utils/color.ts +0 -148
|
@@ -167,59 +167,6 @@ describe('CheckboxInput', () => {
|
|
|
167
167
|
expect(handleChange).not.toHaveBeenCalled();
|
|
168
168
|
});
|
|
169
169
|
|
|
170
|
-
it('forwards data-testid to the input', () => {
|
|
171
|
-
render(
|
|
172
|
-
<CheckboxInput
|
|
173
|
-
label="Accept terms"
|
|
174
|
-
value={false}
|
|
175
|
-
onChange={() => {}}
|
|
176
|
-
data-testid="accept-terms-checkbox"
|
|
177
|
-
/>,
|
|
178
|
-
);
|
|
179
|
-
expect(screen.getByTestId('accept-terms-checkbox')).toBe(
|
|
180
|
-
screen.getByRole('checkbox'),
|
|
181
|
-
);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it('forwards arbitrary data-* attributes to the input', () => {
|
|
185
|
-
render(
|
|
186
|
-
<CheckboxInput
|
|
187
|
-
label="Accept terms"
|
|
188
|
-
value={false}
|
|
189
|
-
onChange={() => {}}
|
|
190
|
-
data-tracking-id="checkbox-42"
|
|
191
|
-
/>,
|
|
192
|
-
);
|
|
193
|
-
expect(screen.getByRole('checkbox')).toHaveAttribute(
|
|
194
|
-
'data-tracking-id',
|
|
195
|
-
'checkbox-42',
|
|
196
|
-
);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('does not let rest props override checked, disabled, or type', () => {
|
|
200
|
-
// Not part of CheckboxInputProps; spread (rather than named attributes)
|
|
201
|
-
// to sidestep JSX excess-property checks the same way a real caller's
|
|
202
|
-
// spread rest-props object would arrive untyped at runtime.
|
|
203
|
-
const foreignAttrs: Record<string, unknown> = {
|
|
204
|
-
checked: false,
|
|
205
|
-
disabled: false,
|
|
206
|
-
type: 'text',
|
|
207
|
-
};
|
|
208
|
-
render(
|
|
209
|
-
<CheckboxInput
|
|
210
|
-
{...foreignAttrs}
|
|
211
|
-
label="Accept terms"
|
|
212
|
-
value={true}
|
|
213
|
-
onChange={() => {}}
|
|
214
|
-
isDisabled
|
|
215
|
-
/>,
|
|
216
|
-
);
|
|
217
|
-
const checkbox = screen.getByRole('checkbox');
|
|
218
|
-
expect(checkbox).toBeChecked();
|
|
219
|
-
expect(checkbox).toBeDisabled();
|
|
220
|
-
expect(checkbox).toHaveAttribute('type', 'checkbox');
|
|
221
|
-
});
|
|
222
|
-
|
|
223
170
|
it('forwards ref correctly', () => {
|
|
224
171
|
const ref = vi.fn();
|
|
225
172
|
render(
|
|
@@ -449,12 +396,7 @@ describe('CheckboxInput', () => {
|
|
|
449
396
|
it('submits under htmlName when checked', () => {
|
|
450
397
|
const {container} = render(
|
|
451
398
|
<form>
|
|
452
|
-
<CheckboxInput
|
|
453
|
-
label="Terms"
|
|
454
|
-
htmlName="terms"
|
|
455
|
-
value={true}
|
|
456
|
-
onChange={() => {}}
|
|
457
|
-
/>
|
|
399
|
+
<CheckboxInput label="Terms" htmlName="terms" value={true} onChange={() => {}} />
|
|
458
400
|
</form>,
|
|
459
401
|
);
|
|
460
402
|
const data = new FormData(container.querySelector('form')!);
|
|
@@ -474,25 +416,16 @@ describe('CheckboxInput', () => {
|
|
|
474
416
|
/>
|
|
475
417
|
</form>,
|
|
476
418
|
);
|
|
477
|
-
expect([
|
|
478
|
-
...new FormData(container.querySelector('form')!).keys(),
|
|
479
|
-
]).toEqual([]);
|
|
419
|
+
expect([...new FormData(container.querySelector('form')!).keys()]).toEqual([]);
|
|
480
420
|
});
|
|
481
421
|
|
|
482
422
|
it('submits nothing when unchecked', () => {
|
|
483
423
|
const {container} = render(
|
|
484
424
|
<form>
|
|
485
|
-
<CheckboxInput
|
|
486
|
-
label="Terms"
|
|
487
|
-
htmlName="terms"
|
|
488
|
-
value={false}
|
|
489
|
-
onChange={() => {}}
|
|
490
|
-
/>
|
|
425
|
+
<CheckboxInput label="Terms" htmlName="terms" value={false} onChange={() => {}} />
|
|
491
426
|
</form>,
|
|
492
427
|
);
|
|
493
|
-
expect([
|
|
494
|
-
...new FormData(container.querySelector('form')!).keys(),
|
|
495
|
-
]).toEqual([]);
|
|
428
|
+
expect([...new FormData(container.querySelector('form')!).keys()]).toEqual([]);
|
|
496
429
|
});
|
|
497
430
|
});
|
|
498
431
|
});
|
|
@@ -395,7 +395,6 @@ export function CheckboxInput({
|
|
|
395
395
|
className,
|
|
396
396
|
style,
|
|
397
397
|
ref,
|
|
398
|
-
...rest
|
|
399
398
|
}: CheckboxInputProps) {
|
|
400
399
|
const id = useId();
|
|
401
400
|
const descriptionID = useId();
|
|
@@ -485,7 +484,6 @@ export function CheckboxInput({
|
|
|
485
484
|
)}>
|
|
486
485
|
<div {...stylex.props(styles.checkboxWrapper, wrapperSizeStyles[size])}>
|
|
487
486
|
<input
|
|
488
|
-
{...rest}
|
|
489
487
|
ref={mergeRefs(
|
|
490
488
|
ref,
|
|
491
489
|
indeterminateRef,
|
|
@@ -31,12 +31,7 @@
|
|
|
31
31
|
import {type ReactNode, type MouseEvent, useRef, type Ref} from 'react';
|
|
32
32
|
import * as stylex from '@stylexjs/stylex';
|
|
33
33
|
import type {StyleXStyles} from '@stylexjs/stylex';
|
|
34
|
-
import {
|
|
35
|
-
borderVars,
|
|
36
|
-
colorVars,
|
|
37
|
-
durationVars,
|
|
38
|
-
easeVars,
|
|
39
|
-
} from '../theme/tokens.stylex';
|
|
34
|
+
import {colorVars, durationVars, easeVars} from '../theme/tokens.stylex';
|
|
40
35
|
import type {SizeValue, SpacingStep} from '../utils/types';
|
|
41
36
|
import {mergeProps, mergeRefs} from '../utils';
|
|
42
37
|
import {Card} from '../Card/Card';
|
|
@@ -71,6 +66,7 @@ const styles = stylex.create({
|
|
|
71
66
|
content: '""',
|
|
72
67
|
position: 'absolute',
|
|
73
68
|
inset: 0,
|
|
69
|
+
borderRadius: 'inherit',
|
|
74
70
|
pointerEvents: 'none',
|
|
75
71
|
transitionProperty: 'background-color',
|
|
76
72
|
transitionDuration: durationVars['--duration-fast'],
|
|
@@ -88,38 +84,6 @@ const styles = stylex.create({
|
|
|
88
84
|
},
|
|
89
85
|
},
|
|
90
86
|
},
|
|
91
|
-
// Borderless variants (everything except `default`): drop the transparent
|
|
92
|
-
// 1px border Card applies. The overlay is inset to the padding box — inside
|
|
93
|
-
// that border — so a transparent border strip stays untinted on hover and
|
|
94
|
-
// reads as a faint ring. With no border, the overlay covers the full box
|
|
95
|
-
// edge-to-edge and the ring disappears.
|
|
96
|
-
borderless: {
|
|
97
|
-
borderWidth: 0,
|
|
98
|
-
},
|
|
99
|
-
// Bordered variant (`default`): draw the 1px border *within* the padding by
|
|
100
|
-
// subtracting the border width from every side. Total inset (border +
|
|
101
|
-
// padding) then equals the borderless variants' padding, so content geometry
|
|
102
|
-
// and outer dimensions stay identical across all variants. The border rests
|
|
103
|
-
// at the subtle token and emphasizes on hover.
|
|
104
|
-
bordered: {
|
|
105
|
-
borderColor: colorVars['--color-border'],
|
|
106
|
-
paddingInlineStart: `calc(var(--container-padding-inline-start) - ${borderVars['--border-width']})`,
|
|
107
|
-
paddingInlineEnd: `calc(var(--container-padding-inline-end) - ${borderVars['--border-width']})`,
|
|
108
|
-
paddingBlockStart: `calc(var(--container-padding-block-start) - ${borderVars['--border-width']})`,
|
|
109
|
-
paddingBlockEnd: `calc(var(--container-padding-block-end) - ${borderVars['--border-width']})`,
|
|
110
|
-
transitionProperty: 'border-color',
|
|
111
|
-
transitionDuration: durationVars['--duration-fast'],
|
|
112
|
-
transitionTimingFunction: easeVars['--ease-standard'],
|
|
113
|
-
},
|
|
114
|
-
// Emphasize the bordered variant's border on hover. Guarded by
|
|
115
|
-
// @media (hover: hover) so touch devices don't get a stuck hover state.
|
|
116
|
-
borderedHoverOnPointer: {
|
|
117
|
-
'@media (hover: hover)': {
|
|
118
|
-
':hover': {
|
|
119
|
-
borderColor: colorVars['--color-border-emphasized'],
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
87
|
disabled: {
|
|
124
88
|
cursor: 'not-allowed',
|
|
125
89
|
opacity: 0.5,
|
|
@@ -284,11 +248,6 @@ export function ClickableCard({
|
|
|
284
248
|
|
|
285
249
|
const isLink = href != null;
|
|
286
250
|
|
|
287
|
-
// Only the `default` variant has a visible border. Card draws a transparent
|
|
288
|
-
// 1px border on every other variant purely to avoid layout jitter; we drop
|
|
289
|
-
// it here so the hover overlay covers the full box with no untinted ring.
|
|
290
|
-
const hasBorder = variant === 'default';
|
|
291
|
-
|
|
292
251
|
return (
|
|
293
252
|
<Card
|
|
294
253
|
ref={mergeRefs(ref, containerRef)}
|
|
@@ -305,10 +264,8 @@ export function ClickableCard({
|
|
|
305
264
|
[
|
|
306
265
|
styles.interactive,
|
|
307
266
|
styles.focusWithin,
|
|
308
|
-
hasBorder ? styles.bordered : styles.borderless,
|
|
309
267
|
!isDisabled && styles.overlay,
|
|
310
268
|
!isDisabled && styles.hoverOnPointer,
|
|
311
|
-
!isDisabled && hasBorder && styles.borderedHoverOnPointer,
|
|
312
269
|
isDisabled && styles.disabled,
|
|
313
270
|
xstyleProp,
|
|
314
271
|
] as unknown as StyleXStyles
|
|
@@ -90,11 +90,6 @@ export const docs = {
|
|
|
90
90
|
type: '(code: string, language: string) => Array<{type: string; start: number; end: number}>',
|
|
91
91
|
description: 'Custom tokenizer override for unsupported languages.',
|
|
92
92
|
},
|
|
93
|
-
{
|
|
94
|
-
name: 'syntaxTheme',
|
|
95
|
-
type: 'SyntaxThemeDefinition',
|
|
96
|
-
description: 'Per-instance syntax theme override. Shorthand for wrapping the block in <SyntaxTheme theme={...}>. Accepts a preset from @astryxdesign/core/theme/syntax or a theme created with defineSyntaxTheme(). Defaults to the nearest SyntaxTheme ancestor or the theme-level syntax colors.',
|
|
97
|
-
},
|
|
98
93
|
{
|
|
99
94
|
name: 'isCollapsible',
|
|
100
95
|
type: 'boolean',
|
|
@@ -140,8 +135,8 @@ export const docs = {
|
|
|
140
135
|
},
|
|
141
136
|
theming: {
|
|
142
137
|
targets: [
|
|
143
|
-
{className: 'astryx-code'
|
|
144
|
-
{className: 'astryx-codeblock', visualProps: ['size', 'language'
|
|
138
|
+
{className: 'astryx-code'},
|
|
139
|
+
{className: 'astryx-codeblock', visualProps: ['size', 'language']},
|
|
145
140
|
],
|
|
146
141
|
},
|
|
147
142
|
usage: {
|
|
@@ -3,21 +3,15 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* @file CodeBlock.test.tsx
|
|
5
5
|
* @input Uses vitest, @testing-library/react, CodeBlock component
|
|
6
|
-
* @output Unit tests for CodeBlock (copy button, collapse, scroll region a11y
|
|
6
|
+
* @output Unit tests for CodeBlock (copy button, collapse, scroll region a11y)
|
|
7
7
|
* @position Testing; validates CodeBlock implementation
|
|
8
8
|
*
|
|
9
9
|
* SYNC: When CodeBlock.tsx changes, update tests to match new behavior
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {describe, it, expect, vi, beforeEach
|
|
13
|
-
import {render, screen, fireEvent
|
|
12
|
+
import {describe, it, expect, vi, beforeEach} from 'vitest';
|
|
13
|
+
import {render, screen, fireEvent} from '@testing-library/react';
|
|
14
14
|
import {CodeBlock} from './CodeBlock';
|
|
15
|
-
import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
|
|
16
|
-
import {dracula} from '../theme/syntax';
|
|
17
|
-
|
|
18
|
-
function politeRegion(): HTMLElement | null {
|
|
19
|
-
return document.querySelector('[data-astryx-live-region="polite"]');
|
|
20
|
-
}
|
|
21
15
|
|
|
22
16
|
// A code sample long enough to exceed the default collapsible threshold (10).
|
|
23
17
|
const LONG_CODE = Array.from(
|
|
@@ -33,10 +27,6 @@ describe('CodeBlock', () => {
|
|
|
33
27
|
});
|
|
34
28
|
});
|
|
35
29
|
|
|
36
|
-
afterEach(() => {
|
|
37
|
-
__resetLiveRegionsForTest();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
30
|
it('renders the code', () => {
|
|
41
31
|
render(<CodeBlock code="const x = 1;" language="javascript" />);
|
|
42
32
|
expect(screen.getByText(/const/)).toBeInTheDocument();
|
|
@@ -63,15 +53,6 @@ describe('CodeBlock', () => {
|
|
|
63
53
|
expect(navigator.clipboard.writeText).toHaveBeenCalledWith('const x = 1;');
|
|
64
54
|
});
|
|
65
55
|
|
|
66
|
-
it('announces "Copied" to a polite live region after copying', async () => {
|
|
67
|
-
render(<CodeBlock code="const x = 1;" language="javascript" />);
|
|
68
|
-
const copyButton = screen.getByRole('button', {name: 'Copy code'});
|
|
69
|
-
fireEvent.click(copyButton);
|
|
70
|
-
await waitFor(() => {
|
|
71
|
-
expect(politeRegion()).toHaveTextContent('Copied');
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
|
|
75
56
|
it('does NOT collapse the block when the copy button is clicked', () => {
|
|
76
57
|
render(
|
|
77
58
|
<CodeBlock
|
|
@@ -131,69 +112,4 @@ describe('CodeBlock', () => {
|
|
|
131
112
|
fireEvent.click(header);
|
|
132
113
|
expect(header).toHaveAttribute('aria-expanded', 'false');
|
|
133
114
|
});
|
|
134
|
-
|
|
135
|
-
it('links the collapsible header to its code region via aria-controls', () => {
|
|
136
|
-
render(
|
|
137
|
-
<CodeBlock
|
|
138
|
-
code={LONG_CODE}
|
|
139
|
-
language="javascript"
|
|
140
|
-
title="example"
|
|
141
|
-
isCollapsible
|
|
142
|
-
/>,
|
|
143
|
-
);
|
|
144
|
-
const header = screen
|
|
145
|
-
.getAllByRole('button')
|
|
146
|
-
.find(el => el.hasAttribute('aria-expanded'))!;
|
|
147
|
-
const controlsId = header.getAttribute('aria-controls');
|
|
148
|
-
// aria-controls must be present and point at the real code region.
|
|
149
|
-
expect(controlsId).toBeTruthy();
|
|
150
|
-
const region = document.getElementById(controlsId as string);
|
|
151
|
-
expect(region).not.toBeNull();
|
|
152
|
-
// The region contains the scrollable code body (role="group").
|
|
153
|
-
expect(region).toContainElement(screen.getByRole('group'));
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
it('keeps aria-controls resolvable when collapsed (region stays mounted)', () => {
|
|
157
|
-
render(
|
|
158
|
-
<CodeBlock
|
|
159
|
-
code={LONG_CODE}
|
|
160
|
-
language="javascript"
|
|
161
|
-
title="example"
|
|
162
|
-
isCollapsible
|
|
163
|
-
/>,
|
|
164
|
-
);
|
|
165
|
-
const header = screen
|
|
166
|
-
.getAllByRole('button')
|
|
167
|
-
.find(el => el.hasAttribute('aria-expanded'))!;
|
|
168
|
-
fireEvent.click(header);
|
|
169
|
-
expect(header).toHaveAttribute('aria-expanded', 'false');
|
|
170
|
-
// The code region uses a CSS grid animation to collapse, so it stays in
|
|
171
|
-
// the DOM — aria-controls stays a valid, resolvable reference (unlike a
|
|
172
|
-
// conditionally-mounted region, which would need a conditional attribute).
|
|
173
|
-
const controlsId = header.getAttribute('aria-controls');
|
|
174
|
-
expect(controlsId).toBeTruthy();
|
|
175
|
-
expect(document.getElementById(controlsId as string)).not.toBeNull();
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('applies a per-instance syntax theme via the syntaxTheme prop', () => {
|
|
179
|
-
const {container} = render(
|
|
180
|
-
<CodeBlock
|
|
181
|
-
code="const x = 1;"
|
|
182
|
-
language="javascript"
|
|
183
|
-
syntaxTheme={dracula}
|
|
184
|
-
/>,
|
|
185
|
-
);
|
|
186
|
-
const wrapper = container.querySelector('[data-astryx-syntax-theme]');
|
|
187
|
-
expect(wrapper).not.toBeNull();
|
|
188
|
-
expect(wrapper).toHaveAttribute('data-astryx-syntax-theme', 'dracula');
|
|
189
|
-
expect(wrapper!.querySelector('pre')).not.toBeNull();
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it('renders no syntax theme wrapper when syntaxTheme is not set', () => {
|
|
193
|
-
const {container} = render(
|
|
194
|
-
<CodeBlock code="const x = 1;" language="javascript" />,
|
|
195
|
-
);
|
|
196
|
-
expect(container.querySelector('[data-astryx-syntax-theme]')).toBeNull();
|
|
197
|
-
expect(container.firstElementChild?.tagName).toBe('PRE');
|
|
198
|
-
});
|
|
199
115
|
});
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
'use client';
|
|
4
4
|
/**
|
|
5
5
|
* @file CodeBlock.tsx
|
|
6
|
-
* @input Uses React, StyleX, theme tokens, CSS Custom Highlight API
|
|
6
|
+
* @input Uses React, StyleX, theme tokens, CSS Custom Highlight API
|
|
7
7
|
* @output Exports CodeBlock component and CodeBlockProps
|
|
8
8
|
* @position Core implementation; read-only syntax-highlighted code display
|
|
9
9
|
*/
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
import {
|
|
12
12
|
useInsertionEffect,
|
|
13
13
|
useEffect,
|
|
14
|
-
useId,
|
|
15
14
|
useRef,
|
|
16
15
|
useState,
|
|
17
16
|
useCallback,
|
|
@@ -33,7 +32,6 @@ import {
|
|
|
33
32
|
easeVars,
|
|
34
33
|
} from '../theme/tokens.stylex';
|
|
35
34
|
import {mergeProps} from '../utils';
|
|
36
|
-
import {useAnnounce} from '../hooks/useAnnounce';
|
|
37
35
|
import {Icon} from '../Icon';
|
|
38
36
|
import {
|
|
39
37
|
tokenize,
|
|
@@ -45,7 +43,6 @@ import type {SyntaxToken, TokenLine} from './tokenizer';
|
|
|
45
43
|
import {ensureHighlightStyles} from './highlightStyles';
|
|
46
44
|
import {applyHighlightRangesChunked} from './highlightRanges';
|
|
47
45
|
import {themeProps} from '../utils/themeProps';
|
|
48
|
-
import {SyntaxTheme, type SyntaxThemeDefinition} from '../theme/syntax';
|
|
49
46
|
|
|
50
47
|
// ---------------------------------------------------------------------------
|
|
51
48
|
// Styles
|
|
@@ -176,17 +173,6 @@ const styles = stylex.create({
|
|
|
176
173
|
headerCollapsible: {
|
|
177
174
|
cursor: 'pointer',
|
|
178
175
|
userSelect: 'none',
|
|
179
|
-
// Restore a keyboard-only focus ring with the standard token/offset so this
|
|
180
|
-
// disclosure control matches the rest of the system (Collapsible, TabMenu);
|
|
181
|
-
// otherwise it falls back to the inconsistent UA default outline.
|
|
182
|
-
outline: {
|
|
183
|
-
default: null,
|
|
184
|
-
':focus-visible': `2px solid ${colorVars['--color-accent']}`,
|
|
185
|
-
},
|
|
186
|
-
outlineOffset: {
|
|
187
|
-
default: '0',
|
|
188
|
-
':focus-visible': '2px',
|
|
189
|
-
},
|
|
190
176
|
},
|
|
191
177
|
gutter: {
|
|
192
178
|
flexShrink: 0,
|
|
@@ -396,14 +382,6 @@ export interface CodeBlockProps extends BaseProps<HTMLPreElement> {
|
|
|
396
382
|
language: string,
|
|
397
383
|
) => {type: string; start: number; end: number}[];
|
|
398
384
|
highlightMode?: 'auto' | 'ranges' | 'spans';
|
|
399
|
-
/**
|
|
400
|
-
* Per-instance syntax theme override. Shorthand for wrapping this block in
|
|
401
|
-
* `<SyntaxTheme theme={...}>` — accepts a preset from
|
|
402
|
-
* `@astryxdesign/core/theme/syntax` or a theme created with
|
|
403
|
-
* `defineSyntaxTheme()`. Without it, the block uses the theme-level syntax
|
|
404
|
-
* colors from the nearest SyntaxTheme ancestor or `defineTheme({ syntax })`.
|
|
405
|
-
*/
|
|
406
|
-
syntaxTheme?: SyntaxThemeDefinition;
|
|
407
385
|
}
|
|
408
386
|
|
|
409
387
|
// ---------------------------------------------------------------------------
|
|
@@ -655,7 +633,6 @@ export function CodeBlock({
|
|
|
655
633
|
container = 'card',
|
|
656
634
|
tokenizer: customTokenizer,
|
|
657
635
|
highlightMode = 'auto',
|
|
658
|
-
syntaxTheme,
|
|
659
636
|
xstyle,
|
|
660
637
|
className,
|
|
661
638
|
style,
|
|
@@ -663,7 +640,6 @@ export function CodeBlock({
|
|
|
663
640
|
...props
|
|
664
641
|
}: CodeBlockProps) {
|
|
665
642
|
const [copied, setCopied] = useState(false);
|
|
666
|
-
const announce = useAnnounce();
|
|
667
643
|
|
|
668
644
|
const useSpans =
|
|
669
645
|
highlightMode === 'spans' ||
|
|
@@ -689,15 +665,12 @@ export function CodeBlock({
|
|
|
689
665
|
try {
|
|
690
666
|
await navigator.clipboard.writeText(code);
|
|
691
667
|
setCopied(true);
|
|
692
|
-
// Swapping the button's aria-label alone isn't reliably announced by
|
|
693
|
-
// screen readers, so confirm the copy via a polite live region.
|
|
694
|
-
announce('Copied');
|
|
695
668
|
onCopy?.();
|
|
696
669
|
setTimeout(() => setCopied(false), 2000);
|
|
697
670
|
} catch {
|
|
698
671
|
// Clipboard failures leave the copied state unchanged.
|
|
699
672
|
}
|
|
700
|
-
}, [code, onCopy
|
|
673
|
+
}, [code, onCopy]);
|
|
701
674
|
|
|
702
675
|
const sizeStyle = size === 'sm' ? styles.sizeSm : styles.sizeMd;
|
|
703
676
|
const gutterSizeStyle = size === 'sm' ? styles.gutterSm : styles.gutterMd;
|
|
@@ -707,11 +680,6 @@ export function CodeBlock({
|
|
|
707
680
|
|
|
708
681
|
const canCollapse = isCollapsible && lines.length >= collapsibleThreshold;
|
|
709
682
|
const [isCollapsed, setIsCollapsed] = useState(false);
|
|
710
|
-
// Links the collapsible header to the code region it shows/hides so assistive
|
|
711
|
-
// tech can move from the button to its controlled content (disclosure
|
|
712
|
-
// pattern). The region stays mounted when collapsed (CSS grid animation), so
|
|
713
|
-
// this is always a resolvable reference — aria-controls can be unconditional.
|
|
714
|
-
const regionId = useId();
|
|
715
683
|
|
|
716
684
|
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
717
685
|
const scrollStyle: CSSProperties | undefined = maxHeight
|
|
@@ -749,7 +717,6 @@ export function CodeBlock({
|
|
|
749
717
|
role={canCollapse ? 'button' : undefined}
|
|
750
718
|
tabIndex={canCollapse ? 0 : undefined}
|
|
751
719
|
aria-expanded={canCollapse ? !isCollapsed : undefined}
|
|
752
|
-
aria-controls={canCollapse ? regionId : undefined}
|
|
753
720
|
onClick={canCollapse ? () => setIsCollapsed(prev => !prev) : undefined}
|
|
754
721
|
onKeyDown={
|
|
755
722
|
canCollapse
|
|
@@ -835,7 +802,7 @@ export function CodeBlock({
|
|
|
835
802
|
</div>
|
|
836
803
|
);
|
|
837
804
|
|
|
838
|
-
|
|
805
|
+
return (
|
|
839
806
|
<pre
|
|
840
807
|
ref={ref}
|
|
841
808
|
{...mergeProps(
|
|
@@ -853,7 +820,6 @@ export function CodeBlock({
|
|
|
853
820
|
{headerEl}
|
|
854
821
|
{canCollapse ? (
|
|
855
822
|
<div
|
|
856
|
-
id={regionId}
|
|
857
823
|
{...stylex.props(
|
|
858
824
|
styles.collapseGrid,
|
|
859
825
|
isCollapsed && styles.collapseGridCollapsed,
|
|
@@ -866,12 +832,6 @@ export function CodeBlock({
|
|
|
866
832
|
{!showHeader && copyButtonEl}
|
|
867
833
|
</pre>
|
|
868
834
|
);
|
|
869
|
-
|
|
870
|
-
return syntaxTheme ? (
|
|
871
|
-
<SyntaxTheme theme={syntaxTheme}>{block}</SyntaxTheme>
|
|
872
|
-
) : (
|
|
873
|
-
block
|
|
874
|
-
);
|
|
875
835
|
}
|
|
876
836
|
|
|
877
837
|
CodeBlock.displayName = 'CodeBlock';
|
|
@@ -16,8 +16,7 @@ export const docs = {
|
|
|
16
16
|
},
|
|
17
17
|
theming: {
|
|
18
18
|
targets: [
|
|
19
|
-
{className: 'astryx-collapsible'
|
|
20
|
-
{className: 'astryx-collapsible-group', visualProps: ['dividers', 'density']},
|
|
19
|
+
{className: 'astryx-collapsible'},
|
|
21
20
|
],
|
|
22
21
|
},
|
|
23
22
|
description: 'A primitive that makes any content collapsible: a trigger button toggles visibility of the content area, managing its own state or deferring to a parent CollapsibleGroup.',
|
|
@@ -70,8 +69,7 @@ export const docs = {
|
|
|
70
69
|
usage: {
|
|
71
70
|
description: 'Collapsible hides and reveals content behind a trigger button. Use it in settings panels, FAQ pages, or detail views to keep the page scannable while letting users drill into sections they care about. Wrap multiple collapsibles in CollapsibleGroup for accordion behavior. For custom collapsible components, use the `useCollapsible` hook directly (`astryx hook useCollapsible`).',
|
|
72
71
|
bestPractices: [
|
|
73
|
-
{ guidance: true, description: '
|
|
74
|
-
{ guidance: true, description: 'Wrap each Collapsible in an Card for visual separation in accordion layouts, or use CollapsibleGroup dividers for flat lists; don\'t combine both.' },
|
|
72
|
+
{ guidance: true, description: 'Wrap each Collapsible in an Card for visual separation in accordion layouts.' },
|
|
75
73
|
{ guidance: true, description: 'Use CollapsibleGroup with type="single" for settings or FAQ pages where only one section should be open at a time.' },
|
|
76
74
|
{ guidance: true, description: 'Use type="multiple" when users need to compare content across sections, like feature lists or pricing tiers.' },
|
|
77
75
|
{ guidance: true, description: 'Start sections open (defaultIsOpen) when the content is likely needed on first view; don\'t make users click to see essential info.' },
|
|
@@ -92,8 +90,7 @@ export const docsZh = {
|
|
|
92
90
|
usage: {
|
|
93
91
|
description: 'Collapsible hides and reveals content behind a trigger button. Use it in settings panels, FAQ pages, or detail views to keep the page scannable while letting users drill into sections they care about. Wrap multiple collapsibles in CollapsibleGroup for accordion behavior.',
|
|
94
92
|
bestPractices: [
|
|
95
|
-
{ guidance: true, description: '
|
|
96
|
-
{ guidance: true, description: 'Wrap each Collapsible in an Card for visual separation in accordion layouts, or use CollapsibleGroup dividers for flat lists; don\'t combine both.' },
|
|
93
|
+
{ guidance: true, description: 'Wrap each Collapsible in an Card for visual separation in accordion layouts.' },
|
|
97
94
|
{ guidance: true, description: 'Use CollapsibleGroup with type="single" for settings or FAQ pages where only one section should be open at a time.' },
|
|
98
95
|
{ guidance: true, description: 'Use type="multiple" when users need to compare content across sections, like feature lists or pricing tiers.' },
|
|
99
96
|
{ guidance: true, description: 'Start sections open (defaultIsOpen) when the content is likely needed on first view; don\'t make users click to see essential info.' },
|
|
@@ -110,8 +107,7 @@ export const docsDense = {
|
|
|
110
107
|
usage: {
|
|
111
108
|
description: 'Collapsible hides and reveals content behind a trigger button. Use in settings, FAQs, or detail views. Wrap in CollapsibleGroup for accordion behavior.',
|
|
112
109
|
bestPractices: [
|
|
113
|
-
{ guidance: true, description: '
|
|
114
|
-
{ guidance: true, description: 'Wrap each Collapsible in an Card for visual separation, or use CollapsibleGroup dividers for flat lists; not both.' },
|
|
110
|
+
{ guidance: true, description: 'Wrap each Collapsible in an Card for visual separation in accordion layouts.' },
|
|
115
111
|
{ guidance: true, description: 'Use CollapsibleGroup with type="single" for settings or FAQ pages where only one section should be open at a time.' },
|
|
116
112
|
{ guidance: true, description: 'Use type="multiple" when users need to compare across sections.' },
|
|
117
113
|
{ guidance: true, description: 'Start sections open (defaultIsOpen) when content is needed on first view.' },
|