Skip to content
Merged
4 changes: 2 additions & 2 deletions bestax-mcp/data/catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,8 @@
"slug": "components/card",
"import": "import { Card } from '@allxsmith/bestax-bulma';",
"compound": true,
"propCount": 49,
"exampleCount": 13
"propCount": 51,
"exampleCount": 14
},
{
"name": "Carousel",
Expand Down
26 changes: 24 additions & 2 deletions bestax-mcp/data/components/Card.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@
{
"title": "Compound (dot-notation) usage",
"code": "<Card header=\"Prop-based header\" textColor=\"primary\" m=\"3\">\n <Card.Content bgColor=\"light\">\n <Paragraph>\n You can mix prop-based and compound component approaches!\n </Paragraph>\n </Card.Content>\n <Card.Footer>\n <Card.FooterItem>Mixed approach example</Card.FooterItem>\n </Card.Footer>\n</Card>"
},
{
"title": "Compound (dot-notation) usage",
"code": "<Card>\n <Card.Header>\n <Card.Header.Title as=\"p\">Component</Card.Header.Title>\n </Card.Header>\n <Card.Content>Card content goes here.</Card.Content>\n <Card.Footer>\n <Card.FooterItem as=\"a\" href=\"#save\">\n Save\n </Card.FooterItem>\n <Card.FooterItem as=\"a\" href=\"#edit\">\n Edit\n </Card.FooterItem>\n </Card.Footer>\n</Card>"
}
],
"accessibility": "- The card image uses an `alt` attribute for screen readers.\n- Use semantic content for the header, body, and footer as needed.",
Expand Down Expand Up @@ -268,6 +272,15 @@
"summary": "Card header title compound component. Renders a `.card-header-title` element.",
"component": null,
"props": [
{
"name": "as",
"type": "'div' | 'p' | 'h2' | 'h3' | 'h4'",
"default": "'div'",
"description": "Element type to render (default: `div`). Bulma's own card markup uses `p`; a heading suits a title that should read as one.",
"required": false,
"inherited": false,
"deprecated": false
},
{
"name": "color",
"type": "Bulma color | 'inherit' | 'current'",
Expand Down Expand Up @@ -327,7 +340,7 @@
}
],
"extraProps": [],
"catchAll": "All standard <div> attributes and Bulma helper props",
"catchAll": "All standard <div> / <h1>–<h6> / <p> attributes and Bulma helper props",
"types": []
},
{
Expand Down Expand Up @@ -567,6 +580,15 @@
"summary": "Card footer item compound component. Wraps children in a `.card-footer-item` span.",
"component": null,
"props": [
{
"name": "as",
"type": "'span' | 'a' | 'button'",
"default": "'span'",
"description": "Element type to render (default: `span`). Bulma's own card markup uses `a`.",
"required": false,
"inherited": false,
"deprecated": false
},
{
"name": "color",
"type": "Bulma color | 'inherit' | 'current'",
Expand Down Expand Up @@ -617,7 +639,7 @@
}
],
"extraProps": [],
"catchAll": "All standard <span> attributes and Bulma helper props",
"catchAll": "All standard <a> / <button> / <span> attributes and Bulma helper props",
"types": []
}
],
Expand Down
6 changes: 3 additions & 3 deletions bestax-mcp/data/skills.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
{
"id": "layout-components",
"file": "references/layout-components.md",
"bytes": 14762
"bytes": 15323
}
],
"examples": [
Expand Down Expand Up @@ -103,7 +103,7 @@
{
"id": "bulma-classes-component-map",
"file": "references/bulma-classes/component-map.md",
"bytes": 14319
"bytes": 14387
},
{
"id": "bulma-classes-prop-map",
Expand All @@ -113,7 +113,7 @@
{
"id": "bulma-classes-unmappables",
"file": "references/bulma-classes/unmappables.md",
"bytes": 15628
"bytes": 15718
},
{
"id": "css-migration",
Expand Down
1 change: 1 addition & 0 deletions bestax-migrate/e2e/as-unions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ describe('the href and `as` tables match what the library accepts', () => {
// the comment on the table, which cites the line in bulma-ui.
expect(HREF_TABLE).toEqual({
Button: 'button',
'Card.FooterItem': 'span',
'Dropdown.Item': 'a',
'Level.Item': 'div',
'Menu.Item': 'a',
Expand Down
1 change: 1 addition & 0 deletions bestax-migrate/src/sources/_shared/polymorphic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ const AS_ANY = new Set(['Button', 'Menu.Item', 'Navbar.Item', 'Navbar.Link']);
*/
const HREF_OK: Record<string, string> = {
Button: 'button',
'Card.FooterItem': 'span',
'Dropdown.Item': 'a',
'Level.Item': 'div',
'Menu.Item': 'a',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@ describe('plan', () => {
expect(todo.message).not.toContain('remove it');
const [inert] = plan(facts('button', 'button', { href: '/x' })).todos;
expect(inert.message).toContain('remove it, then re-run');
// Bulma styles no disabled `.card-footer-item`, so there it is inert.
const [footer] = plan(
facts('span', 'card-footer-item', { disabled: true })
).todos;
expect(footer.rule).toBe('drops:Card.FooterItem');
expect(footer.message).toContain('remove it, then re-run');
});

it('refuses the only child of a component', () => {
Expand Down
30 changes: 28 additions & 2 deletions bestax-migrate/src/sources/bulma-classes/class-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,7 @@ export const ROOTS: Readonly<Record<string, RootEntry>> = {
target: 'Card.Header.Title',
tag: 'div',
modifiers: flags({ 'is-centered': 'centered' }),
ownProps: ['color', 'bgColor', 'textColor', 'centered'],
ownProps: ['as', 'color', 'bgColor', 'textColor', 'centered'],
},
'card-header-icon': {
...BASE,
Expand Down Expand Up @@ -635,7 +635,33 @@ export const ROOTS: Readonly<Record<string, RootEntry>> = {
...BASE,
target: 'Card.FooterItem',
tag: 'span',
ownProps: ['color', 'bgColor', 'textColor'],
// The anchor attributes but `type`, and `rel`, reach only an <a>, and the
// button attributes only a <button> (`STRIP_FROM_NON_ANCHOR` and
// `STRIP_FROM_NON_BUTTON` in bulma-ui's Card.tsx).
dropsAttr: Object.fromEntries([
...[
'href',
'target',
'download',
'hrefLang',
'ping',
'referrerPolicy',
'media',
'rel',
].map(name => [name, { except: ['a'] }]),
...[
'disabled',
'form',
'formAction',
'formEncType',
'formMethod',
'formNoValidate',
'formTarget',
'name',
'value',
].map(name => [name, { except: ['button'] }]),
]),
ownProps: ['as', 'color', 'bgColor', 'textColor'],
},
columns: {
...BASE,
Expand Down
2 changes: 1 addition & 1 deletion bestax-migrate/src/sources/bulma-classes/plan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ export function plan(facts: ElementFacts): Plan {
if (attributes.has(name) && inTagSet(tags, tag)) {
// Bulma greys out `.button[disabled]` on any tag, so there it is not
// inert: dropping it changes how the element looks.
const visible = name === 'disabled';
const visible = name === 'disabled' && target === 'Button';
return refuse(
'drops',
target,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
} from "@allxsmith/bestax-bulma";

// TODO(bestax-migrate): `renderAs` — this bestax component has no `as` prop; restructure the element instead
// TODO(bestax-migrate): bestax `Card.FooterItem` takes no `href` at any `as` -- navigate in `onClick`, or put an <a> inside it -- it read `href="#follow"`
// TODO(bestax-migrate): bestax `Card.FooterItem` renders a <span> unless `as` says otherwise, and only its <a> form carries an `href` -- set `as="a"` to make this a link, or put an <a> inside -- it read `href="#follow"`
export const Profile = () => (
<Hero color="primary" size="fullheight" fullheightWithNavbar>
<Hero.Head>
Expand Down
36 changes: 36 additions & 0 deletions bulma-ui/src/components/Card.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,42 @@ export const HeaderWithIcon: Story = {
},
};

export const BulmaOwnMarkup: Story = {
render: () => (
<Card>
<Card.Header>
<Card.Header.Title as="p">Component</Card.Header.Title>
</Card.Header>
<Card.Content>
<p>
This card matches the markup on Bulma&apos;s own documentation page: a{' '}
<code>{'<p>'}</code> header title (<code>as=&quot;p&quot;</code>) and{' '}
<code>{'<a>'}</code> footer items (<code>as=&quot;a&quot;</code>)
whose whole cell is clickable, rather than a link nested inside a{' '}
<code>{'<span>'}</code>.
</p>
</Card.Content>
<Card.Footer>
<Card.FooterItem as="a" href="#save">
Save
</Card.FooterItem>
<Card.FooterItem as="a" href="#edit">
Edit
</Card.FooterItem>
<Card.FooterItem as="button">Delete</Card.FooterItem>
</Card.Footer>
</Card>
),
parameters: {
docs: {
description: {
story:
"Card.Header.Title and Card.FooterItem take an `as` prop so a card can render the same `p`/`a` markup Bulma's own docs use, instead of always a `div`/`span`.",
},
},
},
};

export const PinnedFooter: Story = {
render: () => (
<Card display="flex" flexDirection="column">
Expand Down
Loading
Loading