@fragments-sdk/ui 0.11.1 → 0.13.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/README.md +15 -0
- package/dist/assets/ui.css +25 -18
- package/dist/blocks/AccountSettings.block.d.ts +1 -1
- package/dist/blocks/ActivityFeed.block.d.ts +1 -1
- package/dist/blocks/ActivityFeedSkeleton.block.d.ts +1 -1
- package/dist/blocks/BlogEditor.block.d.ts +1 -1
- package/dist/blocks/ChatInterface.block.d.ts +1 -1
- package/dist/blocks/ChatMessages.block.d.ts +1 -1
- package/dist/blocks/CheckoutForm.block.d.ts +1 -1
- package/dist/blocks/CommandPalette.block.d.ts +1 -1
- package/dist/blocks/ContactForm.block.d.ts +1 -1
- package/dist/blocks/DashboardLayout.block.d.ts +1 -1
- package/dist/blocks/DashboardPage.block.d.ts +1 -1
- package/dist/blocks/DashboardSkeleton.block.d.ts +1 -1
- package/dist/blocks/DataTable.block.d.ts +1 -1
- package/dist/blocks/EmptyState.block.d.ts +1 -1
- package/dist/blocks/FAQSection.block.d.ts +1 -1
- package/dist/blocks/FeatureGrid.block.d.ts +1 -1
- package/dist/blocks/HeroSection.block.d.ts +1 -1
- package/dist/blocks/LoginForm.block.d.ts +1 -1
- package/dist/blocks/NavigationHeader.block.d.ts +1 -1
- package/dist/blocks/PaginatedTable.block.d.ts +1 -1
- package/dist/blocks/PricingComparison.block.d.ts +1 -1
- package/dist/blocks/ProductCard.block.d.ts +1 -1
- package/dist/blocks/RegistrationForm.block.d.ts +1 -1
- package/dist/blocks/SettingsDrawer.block.d.ts +1 -1
- package/dist/blocks/SettingsPanel.block.d.ts +1 -1
- package/dist/blocks/ShoppingCart.block.d.ts +1 -1
- package/dist/blocks/StatsCard.block.d.ts +1 -1
- package/dist/blocks/StatsCardSkeleton.block.d.ts +1 -1
- package/dist/blocks/TableSkeleton.block.d.ts +1 -1
- package/dist/blocks/ThinkingStates.block.d.ts +1 -1
- package/dist/codeblock.cjs +7 -1
- package/dist/codeblock.cjs.map +1 -1
- package/dist/codeblock.js +7 -1
- package/dist/codeblock.js.map +1 -1
- package/dist/components/Accordion/index.cjs +11 -4
- package/dist/components/Accordion/index.cjs.map +1 -1
- package/dist/components/Accordion/index.d.ts +3 -3
- package/dist/components/Accordion/index.d.ts.map +1 -1
- package/dist/components/Accordion/index.js +11 -4
- package/dist/components/Accordion/index.js.map +1 -1
- package/dist/components/Alert/index.cjs.map +1 -1
- package/dist/components/Alert/index.d.ts +7 -0
- package/dist/components/Alert/index.d.ts.map +1 -1
- package/dist/components/Alert/index.js.map +1 -1
- package/dist/components/Avatar/index.cjs.map +1 -1
- package/dist/components/Avatar/index.d.ts +4 -0
- package/dist/components/Avatar/index.d.ts.map +1 -1
- package/dist/components/Avatar/index.js.map +1 -1
- package/dist/components/Badge/index.cjs.map +1 -1
- package/dist/components/Badge/index.d.ts +12 -0
- package/dist/components/Badge/index.d.ts.map +1 -1
- package/dist/components/Badge/index.js.map +1 -1
- package/dist/components/Button/index.cjs +9 -1
- package/dist/components/Button/index.cjs.map +1 -1
- package/dist/components/Button/index.d.ts +14 -1
- package/dist/components/Button/index.d.ts.map +1 -1
- package/dist/components/Button/index.js +9 -1
- package/dist/components/Button/index.js.map +1 -1
- package/dist/components/Card/index.cjs +2 -1
- package/dist/components/Card/index.cjs.map +1 -1
- package/dist/components/Card/index.d.ts +12 -2
- package/dist/components/Card/index.d.ts.map +1 -1
- package/dist/components/Card/index.js +2 -1
- package/dist/components/Card/index.js.map +1 -1
- package/dist/components/Checkbox/index.cjs.map +1 -1
- package/dist/components/Checkbox/index.d.ts +6 -1
- package/dist/components/Checkbox/index.d.ts.map +1 -1
- package/dist/components/Checkbox/index.js.map +1 -1
- package/dist/components/Chip/index.cjs +2 -1
- package/dist/components/Chip/index.cjs.map +1 -1
- package/dist/components/Chip/index.d.ts +10 -3
- package/dist/components/Chip/index.d.ts.map +1 -1
- package/dist/components/Chip/index.js +2 -1
- package/dist/components/Chip/index.js.map +1 -1
- package/dist/components/CodeBlock/index.d.ts +1 -1
- package/dist/components/CodeBlock/index.d.ts.map +1 -1
- package/dist/components/Collapsible/index.cjs +45 -10
- package/dist/components/Collapsible/index.cjs.map +1 -1
- package/dist/components/Collapsible/index.d.ts +6 -12
- package/dist/components/Collapsible/index.d.ts.map +1 -1
- package/dist/components/Collapsible/index.js +45 -10
- package/dist/components/Collapsible/index.js.map +1 -1
- package/dist/components/Combobox/index.cjs +18 -9
- package/dist/components/Combobox/index.cjs.map +1 -1
- package/dist/components/Combobox/index.d.ts +8 -12
- package/dist/components/Combobox/index.d.ts.map +1 -1
- package/dist/components/Combobox/index.js +18 -9
- package/dist/components/Combobox/index.js.map +1 -1
- package/dist/components/Command/index.cjs +54 -21
- package/dist/components/Command/index.cjs.map +1 -1
- package/dist/components/Command/index.d.ts +2 -2
- package/dist/components/Command/index.d.ts.map +1 -1
- package/dist/components/Command/index.js +54 -21
- package/dist/components/Command/index.js.map +1 -1
- package/dist/components/DataTable/index.cjs +13 -1
- package/dist/components/DataTable/index.cjs.map +1 -1
- package/dist/components/DataTable/index.d.ts.map +1 -1
- package/dist/components/DataTable/index.js +13 -1
- package/dist/components/DataTable/index.js.map +1 -1
- package/dist/components/DatePicker/index.d.ts +2 -3
- package/dist/components/DatePicker/index.d.ts.map +1 -1
- package/dist/components/Dialog/index.cjs +12 -9
- package/dist/components/Dialog/index.cjs.map +1 -1
- package/dist/components/Dialog/index.d.ts +20 -12
- package/dist/components/Dialog/index.d.ts.map +1 -1
- package/dist/components/Dialog/index.js +12 -9
- package/dist/components/Dialog/index.js.map +1 -1
- package/dist/components/Drawer/index.cjs +12 -9
- package/dist/components/Drawer/index.cjs.map +1 -1
- package/dist/components/Drawer/index.d.ts +22 -12
- package/dist/components/Drawer/index.d.ts.map +1 -1
- package/dist/components/Drawer/index.js +12 -9
- package/dist/components/Drawer/index.js.map +1 -1
- package/dist/components/Grid/index.cjs +4 -1
- package/dist/components/Grid/index.cjs.map +1 -1
- package/dist/components/Grid/index.d.ts +6 -2
- package/dist/components/Grid/index.d.ts.map +1 -1
- package/dist/components/Grid/index.js +4 -1
- package/dist/components/Grid/index.js.map +1 -1
- package/dist/components/Input/index.cjs.map +1 -1
- package/dist/components/Input/index.d.ts +15 -1
- package/dist/components/Input/index.d.ts.map +1 -1
- package/dist/components/Input/index.js.map +1 -1
- package/dist/components/Menu/index.cjs +30 -16
- package/dist/components/Menu/index.cjs.map +1 -1
- package/dist/components/Menu/index.d.ts +17 -25
- package/dist/components/Menu/index.d.ts.map +1 -1
- package/dist/components/Menu/index.js +30 -16
- package/dist/components/Menu/index.js.map +1 -1
- package/dist/components/NavigationMenu/NavigationMenuContext.cjs.map +1 -1
- package/dist/components/NavigationMenu/NavigationMenuContext.d.ts +1 -0
- package/dist/components/NavigationMenu/NavigationMenuContext.d.ts.map +1 -1
- package/dist/components/NavigationMenu/NavigationMenuContext.js.map +1 -1
- package/dist/components/NavigationMenu/index.cjs +43 -11
- package/dist/components/NavigationMenu/index.cjs.map +1 -1
- package/dist/components/NavigationMenu/index.d.ts.map +1 -1
- package/dist/components/NavigationMenu/index.js +43 -11
- package/dist/components/NavigationMenu/index.js.map +1 -1
- package/dist/components/NavigationMenu/useNavigationMenu.cjs +2 -0
- package/dist/components/NavigationMenu/useNavigationMenu.cjs.map +1 -1
- package/dist/components/NavigationMenu/useNavigationMenu.d.ts +1 -0
- package/dist/components/NavigationMenu/useNavigationMenu.d.ts.map +1 -1
- package/dist/components/NavigationMenu/useNavigationMenu.js +2 -0
- package/dist/components/NavigationMenu/useNavigationMenu.js.map +1 -1
- package/dist/components/Popover/index.cjs +11 -10
- package/dist/components/Popover/index.cjs.map +1 -1
- package/dist/components/Popover/index.d.ts +17 -12
- package/dist/components/Popover/index.d.ts.map +1 -1
- package/dist/components/Popover/index.js +11 -10
- package/dist/components/Popover/index.js.map +1 -1
- package/dist/components/RadioGroup/index.cjs.map +1 -1
- package/dist/components/RadioGroup/index.d.ts +4 -0
- package/dist/components/RadioGroup/index.d.ts.map +1 -1
- package/dist/components/RadioGroup/index.js.map +1 -1
- package/dist/components/Select/index.cjs +7 -6
- package/dist/components/Select/index.cjs.map +1 -1
- package/dist/components/Select/index.d.ts +20 -9
- package/dist/components/Select/index.d.ts.map +1 -1
- package/dist/components/Select/index.js +7 -6
- package/dist/components/Select/index.js.map +1 -1
- package/dist/components/Sidebar/index.cjs +71 -24
- package/dist/components/Sidebar/index.cjs.map +1 -1
- package/dist/components/Sidebar/index.d.ts +21 -33
- package/dist/components/Sidebar/index.d.ts.map +1 -1
- package/dist/components/Sidebar/index.js +71 -24
- package/dist/components/Sidebar/index.js.map +1 -1
- package/dist/components/Slider/index.cjs +3 -1
- package/dist/components/Slider/index.cjs.map +1 -1
- package/dist/components/Slider/index.d.ts +10 -0
- package/dist/components/Slider/index.d.ts.map +1 -1
- package/dist/components/Slider/index.js +3 -1
- package/dist/components/Slider/index.js.map +1 -1
- package/dist/components/Stack/index.cjs +6 -0
- package/dist/components/Stack/index.cjs.map +1 -1
- package/dist/components/Stack/index.d.ts +12 -6
- package/dist/components/Stack/index.d.ts.map +1 -1
- package/dist/components/Stack/index.js +6 -0
- package/dist/components/Stack/index.js.map +1 -1
- package/dist/components/Tabs/index.cjs.map +1 -1
- package/dist/components/Tabs/index.d.ts +13 -1
- package/dist/components/Tabs/index.d.ts.map +1 -1
- package/dist/components/Tabs/index.js.map +1 -1
- package/dist/components/Text/Text.module.scss.cjs +44 -32
- package/dist/components/Text/Text.module.scss.cjs.map +1 -1
- package/dist/components/Text/Text.module.scss.js +44 -32
- package/dist/components/Text/Text.module.scss.js.map +1 -1
- package/dist/components/Text/index.cjs.map +1 -1
- package/dist/components/Text/index.d.ts +18 -3
- package/dist/components/Text/index.d.ts.map +1 -1
- package/dist/components/Text/index.js.map +1 -1
- package/dist/components/Theme/index.cjs.map +1 -1
- package/dist/components/Theme/index.d.ts +12 -0
- package/dist/components/Theme/index.d.ts.map +1 -1
- package/dist/components/Theme/index.js.map +1 -1
- package/dist/components/Toggle/index.cjs +2 -1
- package/dist/components/Toggle/index.cjs.map +1 -1
- package/dist/components/Toggle/index.d.ts +9 -0
- package/dist/components/Toggle/index.d.ts.map +1 -1
- package/dist/components/Toggle/index.js +2 -1
- package/dist/components/Toggle/index.js.map +1 -1
- package/dist/components/ToggleGroup/index.cjs +4 -1
- package/dist/components/ToggleGroup/index.cjs.map +1 -1
- package/dist/components/ToggleGroup/index.d.ts +13 -4
- package/dist/components/ToggleGroup/index.d.ts.map +1 -1
- package/dist/components/ToggleGroup/index.js +4 -1
- package/dist/components/ToggleGroup/index.js.map +1 -1
- package/dist/components/Tooltip/index.cjs +20 -10
- package/dist/components/Tooltip/index.cjs.map +1 -1
- package/dist/components/Tooltip/index.d.ts +5 -1
- package/dist/components/Tooltip/index.d.ts.map +1 -1
- package/dist/components/Tooltip/index.js +20 -10
- package/dist/components/Tooltip/index.js.map +1 -1
- package/dist/datepicker.cjs +24 -10
- package/dist/datepicker.cjs.map +1 -1
- package/dist/datepicker.js +24 -10
- package/dist/datepicker.js.map +1 -1
- package/dist/index.cjs +4 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/css-warning.cjs +18 -0
- package/dist/utils/css-warning.cjs.map +1 -0
- package/dist/utils/css-warning.d.ts +2 -0
- package/dist/utils/css-warning.d.ts.map +1 -0
- package/dist/utils/css-warning.js +18 -0
- package/dist/utils/css-warning.js.map +1 -0
- package/fragments.json +1 -1
- package/package.json +2 -2
- package/src/components/Accordion/Accordion.test.tsx +33 -0
- package/src/components/Accordion/index.tsx +10 -3
- package/src/components/Alert/index.tsx +7 -0
- package/src/components/Avatar/index.tsx +4 -0
- package/src/components/Badge/Badge.fragment.tsx +10 -2
- package/src/components/Badge/index.tsx +12 -0
- package/src/components/Button/Button.fragment.tsx +12 -2
- package/src/components/Button/Button.test.tsx +16 -0
- package/src/components/Button/index.tsx +27 -2
- package/src/components/Card/Card.fragment.tsx +14 -2
- package/src/components/Card/Card.test.tsx +5 -0
- package/src/components/Card/index.tsx +15 -2
- package/src/components/Checkbox/index.tsx +6 -1
- package/src/components/Chip/Chip.fragment.tsx +12 -2
- package/src/components/Chip/Chip.test.tsx +5 -0
- package/src/components/Chip/index.tsx +14 -4
- package/src/components/CodeBlock/index.tsx +13 -2
- package/src/components/Collapsible/Collapsible.test.tsx +41 -0
- package/src/components/Collapsible/index.tsx +53 -16
- package/src/components/Combobox/Combobox.test.tsx +55 -0
- package/src/components/Combobox/index.tsx +23 -17
- package/src/components/Command/Command.test.tsx +93 -0
- package/src/components/Command/index.tsx +61 -18
- package/src/components/DataTable/DataTable.test.tsx +11 -2
- package/src/components/DataTable/index.tsx +22 -2
- package/src/components/DatePicker/DatePicker.test.tsx +79 -0
- package/src/components/DatePicker/index.tsx +29 -14
- package/src/components/Dialog/Dialog.test.tsx +23 -0
- package/src/components/Dialog/index.tsx +27 -16
- package/src/components/Drawer/Drawer.test.tsx +27 -0
- package/src/components/Drawer/index.tsx +29 -16
- package/src/components/Grid/Grid.fragment.tsx +14 -2
- package/src/components/Grid/Grid.test.tsx +6 -0
- package/src/components/Grid/index.tsx +12 -3
- package/src/components/Input/index.tsx +15 -1
- package/src/components/Menu/index.tsx +35 -30
- package/src/components/NavigationMenu/NavigationMenu.fragment.tsx +1 -1
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +40 -4
- package/src/components/NavigationMenu/NavigationMenuContext.ts +3 -0
- package/src/components/NavigationMenu/index.tsx +49 -13
- package/src/components/NavigationMenu/useNavigationMenu.ts +4 -0
- package/src/components/Popover/Popover.test.tsx +23 -0
- package/src/components/Popover/index.tsx +24 -18
- package/src/components/RadioGroup/index.tsx +4 -0
- package/src/components/Select/Select.test.tsx +41 -0
- package/src/components/Select/index.tsx +24 -12
- package/src/components/Sidebar/Sidebar.test.tsx +83 -4
- package/src/components/Sidebar/index.tsx +87 -45
- package/src/components/Slider/Slider.fragment.tsx +5 -1
- package/src/components/Slider/Slider.test.tsx +6 -0
- package/src/components/Slider/index.tsx +13 -1
- package/src/components/Stack/Stack.fragment.tsx +22 -2
- package/src/components/Stack/Stack.test.tsx +6 -0
- package/src/components/Stack/index.tsx +20 -6
- package/src/components/Tabs/index.tsx +13 -1
- package/src/components/Text/Text.fragment.tsx +10 -8
- package/src/components/Text/Text.module.scss +8 -2
- package/src/components/Text/Text.test.tsx +15 -0
- package/src/components/Text/index.tsx +18 -3
- package/src/components/Theme/index.tsx +12 -0
- package/src/components/Toggle/Toggle.fragment.tsx +5 -1
- package/src/components/Toggle/Toggle.test.tsx +19 -0
- package/src/components/Toggle/index.tsx +11 -1
- package/src/components/ToggleGroup/ToggleGroup.fragment.tsx +5 -2
- package/src/components/ToggleGroup/ToggleGroup.test.tsx +20 -0
- package/src/components/ToggleGroup/index.tsx +15 -4
- package/src/components/Tooltip/Tooltip.test.tsx +17 -0
- package/src/components/Tooltip/index.tsx +58 -34
- package/src/index.ts +6 -0
- package/src/tokens/_seeds.scss +5 -3
- package/src/tokens/_variables.scss +2 -0
- package/src/utils/css-warning.ts +29 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fragments-sdk/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Customizable UI components built on Base UI headless primitives",
|
|
6
6
|
"author": "Conan McNicholl",
|
|
@@ -230,7 +230,7 @@
|
|
|
230
230
|
"vite": "^6.0.0",
|
|
231
231
|
"vitest": "^2.1.8",
|
|
232
232
|
"vitest-axe": "^0.1.0",
|
|
233
|
-
"@fragments-sdk/cli": "0.
|
|
233
|
+
"@fragments-sdk/cli": "0.10.1"
|
|
234
234
|
},
|
|
235
235
|
"files": [
|
|
236
236
|
"src",
|
|
@@ -148,6 +148,22 @@ describe('Accordion', () => {
|
|
|
148
148
|
expect(trigger).toHaveAttribute('aria-expanded', 'false');
|
|
149
149
|
});
|
|
150
150
|
|
|
151
|
+
it('emits undefined when a single collapsible accordion fully closes', async () => {
|
|
152
|
+
const user = userEvent.setup();
|
|
153
|
+
const onValueChange = vi.fn();
|
|
154
|
+
|
|
155
|
+
renderAccordion({
|
|
156
|
+
type: 'single',
|
|
157
|
+
collapsible: true,
|
|
158
|
+
defaultValue: 'one',
|
|
159
|
+
onValueChange,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
await user.click(screen.getByRole('button', { name: /item one/i }));
|
|
163
|
+
|
|
164
|
+
expect(onValueChange).toHaveBeenCalledWith(undefined);
|
|
165
|
+
});
|
|
166
|
+
|
|
151
167
|
it('non-collapsible single type prevents full collapse', async () => {
|
|
152
168
|
const user = userEvent.setup();
|
|
153
169
|
renderAccordion({ type: 'single', collapsible: false, defaultValue: 'one' });
|
|
@@ -160,6 +176,23 @@ describe('Accordion', () => {
|
|
|
160
176
|
expect(trigger).toHaveAttribute('aria-expanded', 'true');
|
|
161
177
|
});
|
|
162
178
|
|
|
179
|
+
it('forwards html props to trigger and content', async () => {
|
|
180
|
+
const user = userEvent.setup();
|
|
181
|
+
render(
|
|
182
|
+
<Accordion>
|
|
183
|
+
<Accordion.Item value="one">
|
|
184
|
+
<Accordion.Trigger data-testid="trigger" data-track="accordion-trigger">Item One</Accordion.Trigger>
|
|
185
|
+
<Accordion.Content data-testid="content" aria-label="Accordion panel">Content One</Accordion.Content>
|
|
186
|
+
</Accordion.Item>
|
|
187
|
+
</Accordion>
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
await user.click(screen.getByTestId('trigger'));
|
|
191
|
+
|
|
192
|
+
expect(screen.getByTestId('trigger')).toHaveAttribute('data-track', 'accordion-trigger');
|
|
193
|
+
expect(screen.getByTestId('content')).toHaveAttribute('aria-label', 'Accordion panel');
|
|
194
|
+
});
|
|
195
|
+
|
|
163
196
|
it('has no accessibility violations', async () => {
|
|
164
197
|
const { container } = renderAccordion({ defaultValue: 'one' });
|
|
165
198
|
await expectNoA11yViolations(container, {
|
|
@@ -19,7 +19,7 @@ export interface AccordionProps extends Omit<React.HTMLAttributes<HTMLDivElement
|
|
|
19
19
|
/** Default value for uncontrolled usage */
|
|
20
20
|
defaultValue?: AccordionValue;
|
|
21
21
|
/** Callback when value changes */
|
|
22
|
-
onValueChange?: (value: AccordionValue) => void;
|
|
22
|
+
onValueChange?: (value: AccordionValue | undefined) => void;
|
|
23
23
|
/** Whether items can be fully collapsed (only for type="single") */
|
|
24
24
|
collapsible?: boolean;
|
|
25
25
|
/**
|
|
@@ -137,7 +137,7 @@ function AccordionRoot({
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
if (onValueChange) {
|
|
140
|
-
onValueChange(type === 'single' ?
|
|
140
|
+
onValueChange(type === 'single' ? newItems[0] : newItems);
|
|
141
141
|
}
|
|
142
142
|
}, [type, currentOpenItems, collapsible, controlledOpenItems, onValueChange]);
|
|
143
143
|
|
|
@@ -186,11 +186,15 @@ function AccordionItem({
|
|
|
186
186
|
function AccordionTrigger({
|
|
187
187
|
children,
|
|
188
188
|
className,
|
|
189
|
+
onClick,
|
|
190
|
+
...htmlProps
|
|
189
191
|
}: AccordionTriggerProps) {
|
|
190
192
|
const { toggle, headingLevel } = useAccordionContext();
|
|
191
193
|
const { value, isOpen, disabled, triggerId, contentId } = useAccordionItemContext();
|
|
192
194
|
|
|
193
|
-
const handleClick = () => {
|
|
195
|
+
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
196
|
+
onClick?.(event);
|
|
197
|
+
if (event.defaultPrevented) return;
|
|
194
198
|
if (!disabled) {
|
|
195
199
|
toggle(value);
|
|
196
200
|
}
|
|
@@ -204,6 +208,7 @@ function AccordionTrigger({
|
|
|
204
208
|
return (
|
|
205
209
|
<HeadingTag className={styles.heading}>
|
|
206
210
|
<BaseCollapsible.Trigger
|
|
211
|
+
{...htmlProps}
|
|
207
212
|
id={triggerId}
|
|
208
213
|
className={classes}
|
|
209
214
|
onClick={handleClick}
|
|
@@ -237,6 +242,7 @@ function AccordionTrigger({
|
|
|
237
242
|
function AccordionContent({
|
|
238
243
|
children,
|
|
239
244
|
className,
|
|
245
|
+
...htmlProps
|
|
240
246
|
}: AccordionContentProps) {
|
|
241
247
|
const { isOpen, triggerId, contentId } = useAccordionItemContext();
|
|
242
248
|
|
|
@@ -244,6 +250,7 @@ function AccordionContent({
|
|
|
244
250
|
|
|
245
251
|
return (
|
|
246
252
|
<BaseCollapsible.Panel
|
|
253
|
+
{...htmlProps}
|
|
247
254
|
id={contentId}
|
|
248
255
|
className={classes}
|
|
249
256
|
data-state={isOpen ? 'open' : 'closed'}
|
|
@@ -10,8 +10,15 @@ import styles from './Alert.module.scss';
|
|
|
10
10
|
|
|
11
11
|
export type AlertSeverity = 'info' | 'success' | 'warning' | 'error';
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Alert for contextual feedback messages (info, success, warning, error).
|
|
15
|
+
* @see https://usefragments.com/components/alert
|
|
16
|
+
*/
|
|
13
17
|
export interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
14
18
|
children: React.ReactNode;
|
|
19
|
+
/** Alert severity level. Controls color and default icon.
|
|
20
|
+
* @default "info"
|
|
21
|
+
* @see https://usefragments.com/components/alert#variants */
|
|
15
22
|
severity?: AlertSeverity;
|
|
16
23
|
}
|
|
17
24
|
|
|
@@ -9,6 +9,10 @@ import styles from './Avatar.module.scss';
|
|
|
9
9
|
|
|
10
10
|
export type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Avatar for user photos, initials, or placeholder icons.
|
|
14
|
+
* @see https://usefragments.com/components/avatar
|
|
15
|
+
*/
|
|
12
16
|
export interface AvatarProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'color'> {
|
|
13
17
|
/** Image source URL */
|
|
14
18
|
src?: string;
|
|
@@ -50,7 +50,7 @@ export default defineFragment({
|
|
|
50
50
|
variant: {
|
|
51
51
|
type: 'enum',
|
|
52
52
|
description: 'Visual style indicating severity or category',
|
|
53
|
-
values: ['default', 'success', 'warning', 'error', 'info'],
|
|
53
|
+
values: ['default', 'success', 'warning', 'error', 'info', 'outline'],
|
|
54
54
|
default: 'default',
|
|
55
55
|
},
|
|
56
56
|
size: {
|
|
@@ -82,7 +82,7 @@ export default defineFragment({
|
|
|
82
82
|
contract: {
|
|
83
83
|
propsSummary: [
|
|
84
84
|
'children: ReactNode - badge label (required)',
|
|
85
|
-
'variant: default|success|warning|error|info - visual style',
|
|
85
|
+
'variant: default|success|warning|error|info|outline - visual style',
|
|
86
86
|
'size: sm|md - badge size',
|
|
87
87
|
'dot: boolean - show status dot indicator',
|
|
88
88
|
'onRemove: () => void - makes badge removable',
|
|
@@ -147,6 +147,14 @@ import { Stack } from '@/components/Stack';
|
|
|
147
147
|
</Stack>
|
|
148
148
|
),
|
|
149
149
|
},
|
|
150
|
+
{
|
|
151
|
+
name: 'Outline',
|
|
152
|
+
description: 'Minimal bordered badge for neutral emphasis',
|
|
153
|
+
code: `import { Badge } from '@/components/Badge';
|
|
154
|
+
|
|
155
|
+
<Badge variant="outline">Outline</Badge>`,
|
|
156
|
+
render: () => <Badge variant="outline">Outline</Badge>,
|
|
157
|
+
},
|
|
150
158
|
{
|
|
151
159
|
name: 'Small Size',
|
|
152
160
|
description: 'Compact badges for dense UIs',
|
|
@@ -4,12 +4,24 @@ import * as React from 'react';
|
|
|
4
4
|
import { Button as BaseButton } from '@base-ui/react/button';
|
|
5
5
|
import styles from './Badge.module.scss';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Badge for status indicators, labels, and counts.
|
|
9
|
+
* @see https://usefragments.com/components/badge
|
|
10
|
+
*/
|
|
7
11
|
export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
8
12
|
children: React.ReactNode;
|
|
13
|
+
/** Visual style variant.
|
|
14
|
+
* @default "default"
|
|
15
|
+
* @see https://usefragments.com/components/badge#variants */
|
|
9
16
|
variant?: 'default' | 'success' | 'warning' | 'error' | 'info' | 'outline';
|
|
17
|
+
/** Badge size.
|
|
18
|
+
* @default "md" */
|
|
10
19
|
size?: 'sm' | 'md' | 'lg';
|
|
20
|
+
/** Show a status dot before the label */
|
|
11
21
|
dot?: boolean;
|
|
22
|
+
/** Icon element rendered before the label */
|
|
12
23
|
icon?: React.ReactNode;
|
|
24
|
+
/** Makes the badge removable. Called when dismiss button is clicked. */
|
|
13
25
|
onRemove?: () => void;
|
|
14
26
|
}
|
|
15
27
|
|
|
@@ -46,7 +46,7 @@ export default defineFragment({
|
|
|
46
46
|
},
|
|
47
47
|
variant: {
|
|
48
48
|
type: 'enum',
|
|
49
|
-
values: ['primary', 'secondary', 'ghost', 'danger'],
|
|
49
|
+
values: ['primary', 'secondary', 'ghost', 'danger', 'outlined', 'outline'],
|
|
50
50
|
default: 'primary',
|
|
51
51
|
description: 'Visual style variant',
|
|
52
52
|
constraints: ['Only one primary button per context'],
|
|
@@ -63,6 +63,11 @@ export default defineFragment({
|
|
|
63
63
|
default: 'button',
|
|
64
64
|
description: 'Render as a native button or anchor element',
|
|
65
65
|
},
|
|
66
|
+
asChild: {
|
|
67
|
+
type: 'boolean',
|
|
68
|
+
default: 'false',
|
|
69
|
+
description: 'Merge button styling onto child element (e.g. Next.js Link)',
|
|
70
|
+
},
|
|
66
71
|
icon: {
|
|
67
72
|
type: 'boolean',
|
|
68
73
|
default: 'false',
|
|
@@ -95,7 +100,7 @@ export default defineFragment({
|
|
|
95
100
|
|
|
96
101
|
contract: {
|
|
97
102
|
propsSummary: [
|
|
98
|
-
'variant: primary|secondary|ghost|danger (default: primary)',
|
|
103
|
+
'variant: primary|secondary|ghost|danger|outlined|outline (default: primary)',
|
|
99
104
|
'size: sm|md|lg (default: md)',
|
|
100
105
|
'disabled: boolean - disables interaction',
|
|
101
106
|
'type: button|submit|reset (default: button)',
|
|
@@ -140,6 +145,11 @@ export default defineFragment({
|
|
|
140
145
|
description: 'Destructive action requiring attention',
|
|
141
146
|
render: () => <Button variant="danger">Delete Item</Button>,
|
|
142
147
|
},
|
|
148
|
+
{
|
|
149
|
+
name: 'Outline',
|
|
150
|
+
description: 'Bordered button with transparent background',
|
|
151
|
+
render: () => <Button variant="outline">View Details</Button>,
|
|
152
|
+
},
|
|
143
153
|
{
|
|
144
154
|
name: 'Sizes',
|
|
145
155
|
description: 'Available size options',
|
|
@@ -46,6 +46,22 @@ describe('Button', () => {
|
|
|
46
46
|
expect(ref).toHaveBeenCalled();
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
+
it('resolves variant="outline" to "outlined"', () => {
|
|
50
|
+
render(<Button variant="outline">Outline</Button>);
|
|
51
|
+
expect(screen.getByRole('button')).toHaveClass('outlined');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('renders as child element when asChild is true', () => {
|
|
55
|
+
render(
|
|
56
|
+
<Button asChild>
|
|
57
|
+
<a href="/test">Link Button</a>
|
|
58
|
+
</Button>
|
|
59
|
+
);
|
|
60
|
+
const link = screen.getByRole('link', { name: 'Link Button' });
|
|
61
|
+
expect(link).toHaveAttribute('href', '/test');
|
|
62
|
+
expect(link).toHaveClass('button');
|
|
63
|
+
});
|
|
64
|
+
|
|
49
65
|
it('has no accessibility violations', async () => {
|
|
50
66
|
const { container } = render(<Button>Accessible</Button>);
|
|
51
67
|
await expectNoA11yViolations(container);
|
|
@@ -4,14 +4,27 @@ import * as React from 'react';
|
|
|
4
4
|
import { Button as BaseButton } from '@base-ui/react/button';
|
|
5
5
|
import styles from './Button.module.scss';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Button props.
|
|
9
|
+
* @see https://usefragments.com/components/button
|
|
10
|
+
*/
|
|
7
11
|
type ButtonBaseProps = {
|
|
8
12
|
children: React.ReactNode;
|
|
9
|
-
|
|
13
|
+
/** Visual style variant. `"outline"` is an alias for `"outlined"`.
|
|
14
|
+
* @default "primary"
|
|
15
|
+
* @see https://usefragments.com/components/button#variants */
|
|
16
|
+
variant?: 'primary' | 'secondary' | 'ghost' | 'danger' | 'outlined' | 'outline';
|
|
17
|
+
/** Button size.
|
|
18
|
+
* @default "md"
|
|
19
|
+
* @see https://usefragments.com/components/button#sizes */
|
|
10
20
|
size?: 'sm' | 'md' | 'lg';
|
|
11
21
|
/** Render as icon-only button (square aspect ratio) */
|
|
12
22
|
icon?: boolean;
|
|
13
23
|
/** Make button full width of container */
|
|
14
24
|
fullWidth?: boolean;
|
|
25
|
+
/** Merge props onto child element instead of rendering a button. Useful for composition with Link components.
|
|
26
|
+
* @see https://usefragments.com/components/button#aschild */
|
|
27
|
+
asChild?: boolean;
|
|
15
28
|
};
|
|
16
29
|
|
|
17
30
|
// Button as native button element
|
|
@@ -36,14 +49,18 @@ const ButtonRoot = React.forwardRef<
|
|
|
36
49
|
>(function Button(props, ref) {
|
|
37
50
|
const {
|
|
38
51
|
children,
|
|
39
|
-
variant = 'primary',
|
|
52
|
+
variant: variantProp = 'primary',
|
|
40
53
|
size = 'md',
|
|
41
54
|
icon = false,
|
|
42
55
|
fullWidth = false,
|
|
56
|
+
asChild = false,
|
|
43
57
|
className,
|
|
44
58
|
...rest
|
|
45
59
|
} = props;
|
|
46
60
|
|
|
61
|
+
// Resolve alias: "outline" → "outlined"
|
|
62
|
+
const variant = variantProp === 'outline' ? 'outlined' : variantProp;
|
|
63
|
+
|
|
47
64
|
const classNames = [
|
|
48
65
|
styles.button,
|
|
49
66
|
styles[size],
|
|
@@ -55,6 +72,14 @@ const ButtonRoot = React.forwardRef<
|
|
|
55
72
|
.filter(Boolean)
|
|
56
73
|
.join(' ');
|
|
57
74
|
|
|
75
|
+
// asChild: merge button styling onto child element (e.g. Next.js Link)
|
|
76
|
+
if (asChild && React.isValidElement(children)) {
|
|
77
|
+
return React.cloneElement(children as React.ReactElement<Record<string, unknown>>, {
|
|
78
|
+
className: [classNames, (children.props as Record<string, unknown>).className].filter(Boolean).join(' '),
|
|
79
|
+
ref,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
58
83
|
// Render as anchor
|
|
59
84
|
if (props.as === 'a') {
|
|
60
85
|
const { as: _as, ...anchorProps } = rest as ButtonAsAnchorProps & { as?: 'a' };
|
|
@@ -44,7 +44,7 @@ export default defineFragment({
|
|
|
44
44
|
},
|
|
45
45
|
variant: {
|
|
46
46
|
type: 'enum',
|
|
47
|
-
values: ['default', 'outlined', 'elevated'],
|
|
47
|
+
values: ['default', 'outlined', 'outline', 'elevated'],
|
|
48
48
|
default: 'default',
|
|
49
49
|
description: 'Visual style of the card surface',
|
|
50
50
|
constraints: ['Use "elevated" sparingly to maintain visual hierarchy'],
|
|
@@ -76,7 +76,7 @@ export default defineFragment({
|
|
|
76
76
|
|
|
77
77
|
contract: {
|
|
78
78
|
propsSummary: [
|
|
79
|
-
'variant: default|outlined|elevated (default: default)',
|
|
79
|
+
'variant: default|outlined|outline|elevated (default: default)',
|
|
80
80
|
'padding: none|sm|md|lg (default: md)',
|
|
81
81
|
'onClick: () => void - makes card interactive',
|
|
82
82
|
'Sub-components: Card.Header, Card.Title, Card.Description, Card.Body, Card.Footer',
|
|
@@ -130,6 +130,18 @@ export default defineFragment({
|
|
|
130
130
|
</Card>
|
|
131
131
|
),
|
|
132
132
|
},
|
|
133
|
+
{
|
|
134
|
+
name: 'Outline',
|
|
135
|
+
description: 'Card with border, using the "outline" alias for "outlined"',
|
|
136
|
+
render: () => (
|
|
137
|
+
<Card variant="outline">
|
|
138
|
+
<Card.Header>
|
|
139
|
+
<Card.Title>Outline Card</Card.Title>
|
|
140
|
+
</Card.Header>
|
|
141
|
+
<Card.Body>Uses the Radix/Shadcn-style alias.</Card.Body>
|
|
142
|
+
</Card>
|
|
143
|
+
),
|
|
144
|
+
},
|
|
133
145
|
{
|
|
134
146
|
name: 'Elevated',
|
|
135
147
|
description: 'Card with prominent shadow for emphasis',
|
|
@@ -57,6 +57,11 @@ describe('Card', () => {
|
|
|
57
57
|
expect(screen.getByRole('button')).toHaveClass('interactive');
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
+
it('resolves variant="outline" to "outlined"', () => {
|
|
61
|
+
render(<Card variant="outline">Content</Card>);
|
|
62
|
+
expect(screen.getByRole('article')).toHaveClass('outlined');
|
|
63
|
+
});
|
|
64
|
+
|
|
60
65
|
it('has no accessibility violations', async () => {
|
|
61
66
|
const { container } = render(
|
|
62
67
|
<Card>
|
|
@@ -7,10 +7,20 @@ import styles from './Card.module.scss';
|
|
|
7
7
|
// Types
|
|
8
8
|
// ============================================
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Card container for grouping related content.
|
|
12
|
+
* @see https://usefragments.com/components/card
|
|
13
|
+
*/
|
|
10
14
|
export interface CardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onClick'> {
|
|
11
15
|
children: React.ReactNode;
|
|
12
|
-
variant
|
|
16
|
+
/** Visual style variant. `"outline"` is an alias for `"outlined"`.
|
|
17
|
+
* @default "default"
|
|
18
|
+
* @see https://usefragments.com/components/card#variants */
|
|
19
|
+
variant?: 'default' | 'outlined' | 'outline' | 'elevated';
|
|
20
|
+
/** Inner padding.
|
|
21
|
+
* @default "md" */
|
|
13
22
|
padding?: 'none' | 'sm' | 'md' | 'lg';
|
|
23
|
+
/** Makes the card interactive (clickable) */
|
|
14
24
|
onClick?: () => void;
|
|
15
25
|
}
|
|
16
26
|
|
|
@@ -71,7 +81,7 @@ const paddingMap = {
|
|
|
71
81
|
|
|
72
82
|
function CardRoot({
|
|
73
83
|
children,
|
|
74
|
-
variant = 'default',
|
|
84
|
+
variant: variantProp = 'default',
|
|
75
85
|
padding = 'md',
|
|
76
86
|
onClick,
|
|
77
87
|
className,
|
|
@@ -80,6 +90,9 @@ function CardRoot({
|
|
|
80
90
|
'aria-describedby': ariaDescribedBy,
|
|
81
91
|
...htmlProps
|
|
82
92
|
}: CardProps) {
|
|
93
|
+
// Resolve alias: "outline" → "outlined"
|
|
94
|
+
const variant = variantProp === 'outline' ? 'outlined' : variantProp;
|
|
95
|
+
|
|
83
96
|
const isInteractive = !!onClick;
|
|
84
97
|
|
|
85
98
|
const classes = [
|
|
@@ -8,6 +8,10 @@ import styles from './Checkbox.module.scss';
|
|
|
8
8
|
// Types
|
|
9
9
|
// ============================================
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Checkbox for boolean or indeterminate selections in forms.
|
|
13
|
+
* @see https://usefragments.com/components/checkbox
|
|
14
|
+
*/
|
|
11
15
|
export interface CheckboxProps extends Omit<React.HTMLAttributes<HTMLLabelElement>, 'onChange' | 'defaultChecked'> {
|
|
12
16
|
/** Whether the checkbox is checked */
|
|
13
17
|
checked?: boolean;
|
|
@@ -21,7 +25,8 @@ export interface CheckboxProps extends Omit<React.HTMLAttributes<HTMLLabelElemen
|
|
|
21
25
|
disabled?: boolean;
|
|
22
26
|
/** Whether the checkbox is required */
|
|
23
27
|
required?: boolean;
|
|
24
|
-
/** Size variant
|
|
28
|
+
/** Size variant.
|
|
29
|
+
* @default "md" */
|
|
25
30
|
size?: 'sm' | 'md' | 'lg';
|
|
26
31
|
/** Label text */
|
|
27
32
|
label?: string;
|
|
@@ -50,7 +50,7 @@ export default defineFragment({
|
|
|
50
50
|
variant: {
|
|
51
51
|
type: 'enum',
|
|
52
52
|
description: 'Visual style variant',
|
|
53
|
-
values: ['filled', 'outlined', 'soft'],
|
|
53
|
+
values: ['filled', 'outlined', 'outline', 'soft'],
|
|
54
54
|
default: 'filled',
|
|
55
55
|
},
|
|
56
56
|
size: {
|
|
@@ -91,7 +91,7 @@ export default defineFragment({
|
|
|
91
91
|
contract: {
|
|
92
92
|
propsSummary: [
|
|
93
93
|
'children: ReactNode - chip label (required)',
|
|
94
|
-
'variant: filled|outlined|soft - visual style',
|
|
94
|
+
'variant: filled|outlined|outline|soft - visual style',
|
|
95
95
|
'size: sm|md - chip size',
|
|
96
96
|
'selected: boolean - selection state',
|
|
97
97
|
'icon/avatar: ReactNode - leading visual',
|
|
@@ -113,6 +113,16 @@ export default defineFragment({
|
|
|
113
113
|
description: 'Basic filled chip',
|
|
114
114
|
render: () => <Chip>Default</Chip>,
|
|
115
115
|
},
|
|
116
|
+
{
|
|
117
|
+
name: 'Outline',
|
|
118
|
+
description: 'Chip with border using the "outline" alias for "outlined"',
|
|
119
|
+
render: () => (
|
|
120
|
+
<div style={{ display: 'flex', gap: '8px' }}>
|
|
121
|
+
<Chip variant="outline">Outline</Chip>
|
|
122
|
+
<Chip variant="outline" selected>Outline Selected</Chip>
|
|
123
|
+
</div>
|
|
124
|
+
),
|
|
125
|
+
},
|
|
116
126
|
{
|
|
117
127
|
name: 'Selected',
|
|
118
128
|
description: 'Chip in selected state across variants',
|
|
@@ -43,6 +43,11 @@ describe('Chip', () => {
|
|
|
43
43
|
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
+
it('resolves variant="outline" to "outlined"', () => {
|
|
47
|
+
render(<Chip variant="outline">Outline</Chip>);
|
|
48
|
+
expect(screen.getByRole('button', { name: 'Outline' })).toHaveClass('outlined');
|
|
49
|
+
});
|
|
50
|
+
|
|
46
51
|
it('has no accessibility violations', async () => {
|
|
47
52
|
const { container } = render(<Chip>Accessible chip</Chip>);
|
|
48
53
|
await expectNoA11yViolations(container);
|
|
@@ -3,11 +3,18 @@
|
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import styles from './Chip.module.scss';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Chip for selections, filters, and tags. Use with Chip.Group for multi-select.
|
|
8
|
+
* @see https://usefragments.com/components/chip
|
|
9
|
+
*/
|
|
6
10
|
export interface ChipProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
|
|
7
11
|
children: React.ReactNode;
|
|
8
|
-
/** Visual style variant
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
/** Visual style variant. `"outline"` is an alias for `"outlined"`.
|
|
13
|
+
* @default "filled"
|
|
14
|
+
* @see https://usefragments.com/components/chip#variants */
|
|
15
|
+
variant?: 'filled' | 'outlined' | 'outline' | 'soft';
|
|
16
|
+
/** Chip size.
|
|
17
|
+
* @default "md" */
|
|
11
18
|
size?: 'sm' | 'md' | 'lg';
|
|
12
19
|
/** Whether the chip is selected */
|
|
13
20
|
selected?: boolean;
|
|
@@ -36,7 +43,7 @@ const ChipBase = React.forwardRef<HTMLButtonElement, ChipProps>(
|
|
|
36
43
|
function Chip(
|
|
37
44
|
{
|
|
38
45
|
children,
|
|
39
|
-
variant = 'filled',
|
|
46
|
+
variant: variantProp = 'filled',
|
|
40
47
|
size = 'md',
|
|
41
48
|
selected = false,
|
|
42
49
|
disabled = false,
|
|
@@ -50,6 +57,9 @@ const ChipBase = React.forwardRef<HTMLButtonElement, ChipProps>(
|
|
|
50
57
|
},
|
|
51
58
|
ref
|
|
52
59
|
) {
|
|
60
|
+
// Resolve alias: "outline" → "outlined"
|
|
61
|
+
const variant = variantProp === 'outline' ? 'outlined' : variantProp;
|
|
62
|
+
|
|
53
63
|
const classes = [
|
|
54
64
|
styles.chip,
|
|
55
65
|
styles[size],
|
|
@@ -35,7 +35,9 @@ import "../../styles/globals.scss";
|
|
|
35
35
|
export type CodeBlockLanguage =
|
|
36
36
|
| "tsx"
|
|
37
37
|
| "typescript"
|
|
38
|
+
| "ts"
|
|
38
39
|
| "javascript"
|
|
40
|
+
| "js"
|
|
39
41
|
| "jsx"
|
|
40
42
|
| "bash"
|
|
41
43
|
| "shell"
|
|
@@ -64,7 +66,15 @@ export type CodeBlockLanguage =
|
|
|
64
66
|
| "sql"
|
|
65
67
|
| "graphql"
|
|
66
68
|
| "diff"
|
|
67
|
-
| "plaintext"
|
|
69
|
+
| "plaintext"
|
|
70
|
+
| "text";
|
|
71
|
+
|
|
72
|
+
/** Resolves language aliases to their canonical Shiki names */
|
|
73
|
+
const LANGUAGE_ALIASES: Partial<Record<CodeBlockLanguage, string>> = {
|
|
74
|
+
ts: "typescript",
|
|
75
|
+
js: "javascript",
|
|
76
|
+
text: "plaintext",
|
|
77
|
+
};
|
|
68
78
|
|
|
69
79
|
/** Available syntax highlighting themes */
|
|
70
80
|
export type CodeBlockTheme =
|
|
@@ -591,7 +601,8 @@ const CodeBlockBase = React.forwardRef<HTMLDivElement, CodeBlockProps>(function
|
|
|
591
601
|
}
|
|
592
602
|
|
|
593
603
|
try {
|
|
594
|
-
const
|
|
604
|
+
const resolvedLang = LANGUAGE_ALIASES[language] || language;
|
|
605
|
+
const html = await _codeToHtml(visibleCode, { lang: resolvedLang, theme });
|
|
595
606
|
return processShikiHtml(html, {
|
|
596
607
|
showLineNumbers,
|
|
597
608
|
startLineNumber,
|
|
@@ -96,6 +96,47 @@ describe('Collapsible', () => {
|
|
|
96
96
|
expect(screen.queryByText('Collapsible content here')).not.toBeInTheDocument();
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
+
it('composes child handlers when trigger uses asChild', async () => {
|
|
100
|
+
const user = userEvent.setup();
|
|
101
|
+
const childClick = vi.fn();
|
|
102
|
+
const childKeyDown = vi.fn();
|
|
103
|
+
|
|
104
|
+
render(
|
|
105
|
+
<Collapsible>
|
|
106
|
+
<Collapsible.Trigger asChild>
|
|
107
|
+
<button onClick={childClick} onKeyDown={childKeyDown}>Toggle</button>
|
|
108
|
+
</Collapsible.Trigger>
|
|
109
|
+
<Collapsible.Content>Collapsible content here</Collapsible.Content>
|
|
110
|
+
</Collapsible>
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const trigger = screen.getByRole('button', { name: /toggle/i });
|
|
114
|
+
await user.click(trigger);
|
|
115
|
+
expect(childClick).toHaveBeenCalled();
|
|
116
|
+
expect(screen.getByText('Collapsible content here')).toBeInTheDocument();
|
|
117
|
+
|
|
118
|
+
await user.keyboard('{Enter}');
|
|
119
|
+
expect(childKeyDown).toHaveBeenCalled();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('forwards html props to root, trigger, and content', async () => {
|
|
123
|
+
const user = userEvent.setup();
|
|
124
|
+
render(
|
|
125
|
+
<Collapsible data-testid="root" data-track="collapsible-root">
|
|
126
|
+
<Collapsible.Trigger data-testid="trigger" aria-label="Toggle section">Toggle</Collapsible.Trigger>
|
|
127
|
+
<Collapsible.Content data-testid="content" data-panel="details">
|
|
128
|
+
Collapsible content here
|
|
129
|
+
</Collapsible.Content>
|
|
130
|
+
</Collapsible>
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
await user.click(screen.getByTestId('trigger'));
|
|
134
|
+
|
|
135
|
+
expect(screen.getByTestId('root')).toHaveAttribute('data-track', 'collapsible-root');
|
|
136
|
+
expect(screen.getByTestId('trigger')).toHaveAttribute('aria-label', 'Toggle section');
|
|
137
|
+
expect(screen.getByTestId('content')).toHaveAttribute('data-panel', 'details');
|
|
138
|
+
});
|
|
139
|
+
|
|
99
140
|
it('has no accessibility violations', async () => {
|
|
100
141
|
const { container } = renderCollapsible({ defaultOpen: true });
|
|
101
142
|
await expectNoA11yViolations(container);
|