### Install Workbench Source: https://project.pages.drupalcode.org/canvas/code-components/workbench Install Workbench using npm. After installation, you can run it from your project root. ```bash npm install @drupal-canvas/workbench ``` ```bash npx canvas-workbench ``` -------------------------------- ### Example: Index-style component file structure Source: https://project.pages.drupalcode.org/canvas/code-components/workbench/mocks Illustrates the placement of the mocks.json file for an index-style component. ```text src/components/hero/ index.tsx component.yml mocks.json ``` -------------------------------- ### Example: Named component file structure Source: https://project.pages.drupalcode.org/canvas/code-components/workbench/mocks Illustrates the placement of the [component-name].mocks.json file for a named component. ```text src/components/ card.tsx card.component.yml card.mocks.json ``` -------------------------------- ### Set Default Prop Values Source: https://project.pages.drupalcode.org/canvas/sdc-components/validations Use the 'examples' property to set default values for props. The first item in the 'examples' array is used as the default. 'examples' must be an array, and is mandatory when a prop is required. ```yaml props: type: object text: type: string examples: - "Hot" - "New" description: "The text displayed in the badge." ``` -------------------------------- ### SDC Component Structure Example Source: https://project.pages.drupalcode.org/canvas/sdc-components Illustrates the typical file structure for a Single Directory Component within a Drupal theme. ```yaml my_theme/ └── components/ └── singer/ ├── singer.component.yml ├── singer.twig ├── singer.css └── micro.jpg ``` -------------------------------- ### Example Page JSON Source: https://project.pages.drupalcode.org/canvas/code-components/workbench/pages This JSON defines the structure and content for a page preview in Workbench. It includes a title and a tree of elements, each with a type, props, and slots. ```json { "title": "Home", "elements": { "header": { "type": "js.header", "props": { "darkVariant": true, "backgroundColor": "crust" }, "slots": { "branding": ["header-logo"], "navigation": ["header-navigation"] } }, "header-logo": { "type": "js.logo", "props": { "linkToFrontPage": true } }, "header-navigation": { "type": "js.navigation", "props": {} }, "card-grid": { "type": "grid", "props": { "columns": 3, "gap": "md" }, "slots": { "items": ["card-1", "card-2", "card-3"] } }, "card-1": { "type": "card", "props": { "featured": true }, "slots": { "header": ["card-1-header"], "content": ["card-1-content"], "footer": ["card-1-footer"] } }, "card-1-header": { "type": "heading", "props": { "text": "Featured article" } }, "card-1-content": { "type": "text", "props": { "content": "Learn how editorial teams use Canvas to review, refine, and publish AI-assisted content." } }, "card-1-footer": { "type": "button-group", "slots": { "items": ["card-1-primary-action", "card-1-secondary-action"] } }, "card-1-primary-action": { "type": "button", "props": { "label": "Read more", "href": "/articles/canvas-workflows" } }, "card-1-secondary-action": { "type": "button", "props": { "label": "Save for later", "href": "/saved" } }, "card-2": { "type": "card", "props": { "featured": false }, "slots": { "header": ["card-2-header"], "content": ["card-2-content"], "footer": ["card-2-footer"] } }, "card-2-header": { "type": "heading", "props": { "text": "Documentation update" } }, "card-2-content": { "type": "text", "props": { "content": "Review the latest guidance for authoring Workbench component mocks." } }, "card-2-footer": { "type": "button", "props": { "label": "Open docs", "href": "/docs/workbench-mocks" } }, "card-3": { "type": "card", "props": { "featured": false }, "slots": { "header": ["card-3-header"], "content": ["card-3-content"], "footer": ["card-3-footer"] } }, "card-3-header": { "type": "heading", "props": { "text": "Team workflow" } }, "card-3-content": { "type": "text", "props": { "content": "Coordinate AI drafting, editorial review, and publishing approvals in one place." } }, "card-3-footer": { "type": "button", "props": { "label": "See workflow", "href": "/workflow" } } } } ``` -------------------------------- ### Page File Structure Source: https://project.pages.drupalcode.org/canvas/code-components/workbench/pages Example of a page file structure for Workbench. Place page files in the top-level `pages` directory. Workbench discovers `pages/*.json` files. ```json pages/ home.json about.json ``` -------------------------------- ### Define a React Component with Props Source: https://project.pages.drupalcode.org/canvas/code-components/props This example shows a basic React component that accepts a 'firstName' prop and renders a greeting. Props are destructured directly in the function signature. ```javascript const Hello = ({ firstName }) => { return
Hello, {firstName}!
; }; export default Hello; ``` -------------------------------- ### Get site branding data Source: https://project.pages.drupalcode.org/canvas/code-components/data-fetching Access site branding information, such as the site name, using the getSiteData utility. ```javascript import { getSiteData } from 'drupal-canvas'; const { siteName } = getSiteData().branding; ``` -------------------------------- ### Define Card Slots with Descriptions Source: https://project.pages.drupalcode.org/canvas/ai-assistant Use this YAML structure to define slots for a card component. Explicitly describe the intended content for each slot, like 'content' for text and 'actions' for buttons, to guide the AI. ```yaml slots: content: title: Card Description description: > The description or body text of the card. Don't place buttons or badges here, use the actions slot for that. actions: title: Actions description: The actions of the card, use buttons or badge components here. ``` -------------------------------- ### Get page data Source: https://project.pages.drupalcode.org/canvas/code-components/data-fetching Access information about the current page, such as title, breadcrumbs, and the main entity, using the getPageData utility. Results are viewable in the Data Fetch pane. ```javascript import { getPageData } from 'drupal-canvas'; const { pageTitle, breadcrumbs, mainEntity } = getPageData(); ``` -------------------------------- ### Run Workbench Source: https://project.pages.drupalcode.org/canvas/code-components/workbench Run Workbench from your project root using npx. This command initiates the local preview and development server. ```bash npx @drupal-canvas/workbench@latest ``` -------------------------------- ### Build Component Preview Source: https://project.pages.drupalcode.org/canvas/code-components/workbench/preview-build Use this command to export a standalone preview artifact for a specific component. Ensure the `--component-path` points to the component's YAML file and `--out-dir` specifies the desired output directory. ```bash npx canvas-workbench preview-build --component-path components/card/component.yml --out-dir .canvas-preview/card ``` -------------------------------- ### Create a Basic React Component Source: https://project.pages.drupalcode.org/canvas/code-components Define a new component with a default export. Ensure the component accepts props and renders JSX. This serves as a starter template for new components. ```javascript const Component = ({ text = 'Component' }) => { return
{text}
; }; export default Component; ``` -------------------------------- ### Build Page Preview Source: https://project.pages.drupalcode.org/canvas/code-components/workbench/preview-build Use this command to export a standalone preview artifact for a specific page. Ensure the `--page-path` points to the page's JSON file and `--out-dir` specifies the desired output directory. ```bash npx canvas-workbench preview-build --page-path pages/home.json --out-dir .canvas-preview/home ``` -------------------------------- ### Image Component with Sizes Prop Source: https://project.pages.drupalcode.org/canvas/code-components/responsive-images Configure the `sizes` prop to define image download sizes across different breakpoints. This impacts performance for images using `fill` or styled responsively. ```javascript {photo.alt} ``` -------------------------------- ### Advanced Mock Format for Workbench Source: https://project.pages.drupalcode.org/canvas/code-components/workbench/mocks Use the advanced format when the preview needs full control over the rendered root element. This format is useful when you need to render a component inside other components to verify how it behaves in context. It allows you to define the full element tree, including the root element and any elements referenced by slots. ```json [ { "name": "In grid", "root": "card-grid", "elements": { "card-grid": { "type": "grid", "props": { "columns": 3, "gap": "md" }, "slots": { "items": ["card-1", "card-2", "card-3"] } }, "card-1": { "type": "card", "props": { "featured": true }, "slots": { "header": ["card-1-header"], "content": ["card-1-content"], "footer": ["card-1-footer"] } }, "card-1-header": { "type": "heading", "props": { "text": "Featured article" } }, "card-1-content": { "type": "text", "props": { "content": "Learn how editorial teams use Canvas to review, refine, and publish AI-assisted content." } }, "card-1-footer": { "type": "button-group", "slots": { "items": ["card-1-primary-action", "card-1-secondary-action"] } }, "card-1-primary-action": { "type": "button", "props": { "label": "Read more", "href": "/articles/canvas-workflows" } }, "card-1-secondary-action": { "type": "button", "props": { "label": "Save for later", "href": "/saved" } }, "card-2": { "type": "card", "props": { "featured": false }, "slots": { "header": ["card-2-header"], "content": ["card-2-content"], "footer": ["card-2-footer"] } }, "card-2-header": { "type": "heading", "props": { "text": "Documentation update" } }, "card-2-content": { "type": "text", "props": { "content": "Review the latest guidance for authoring Workbench component mocks." } }, "card-2-footer": { "type": "button", "props": { "label": "Open docs", "href": "/docs/workbench-mocks" } }, "card-3": { "type": "card", "props": { "featured": false }, "slots": { "header": ["card-3-header"], "content": ["card-3-content"], "footer": ["card-3-footer"] } }, "card-3-header": { "type": "heading", "props": { "text": "Team workflow" } }, "card-3-content": { "type": "text", "props": { "content": "Coordinate AI drafting, editorial review, and publishing approvals in one place." } }, "card-3-footer": { "type": "button", "props": { "label": "See workflow", "href": "/workflow" } } } } ] ``` -------------------------------- ### Define Link Prop Schema Source: https://project.pages.drupalcode.org/canvas/code-components/cli-tool/prop-schemas Use this schema for URL or URI references. It accepts absolute URLs with `format: uri` or both absolute and relative URLs with `format: uri-reference`. ```yaml type: string format: uri-reference examples: - https://example.com ``` -------------------------------- ### Fetch data with SWR Source: https://project.pages.drupalcode.org/canvas/code-components/data-fetching Use the SWR hook for general data fetching. Ensure a fetcher function is defined and imported. Data is displayed in the 'Data Fetch' pane. ```javascript import useSWR from 'swr'; export default function Profile() { const { data, error, isLoading } = useSWR( 'https://my-site.com/api/user', fetcher, ); if (error) return
failed to load
; if (isLoading) return
loading...
; return
hello {data.name}!
; } ``` -------------------------------- ### Mock entry with props only Source: https://project.pages.drupalcode.org/canvas/code-components/workbench/mocks Defines a mock entry using only the 'props' field for previewing component prop variations. The 'name' field labels the preview tab. ```json [ { "name": "Centered", "props": { "text": "Publish AI-assisted content with editorial control", "eyebrow": "Build faster", "textAlign": "center" } } ] ``` -------------------------------- ### Define Link Prop (URI Format) Source: https://project.pages.drupalcode.org/canvas/sdc-components/props Use for absolute URLs only. The 'format: uri' ensures the value includes a scheme. ```yaml link: type: string format: uri title: 'Link URL' examples: ['https://example.com/path'] ``` -------------------------------- ### Import React Package with esm.sh Source: https://project.pages.drupalcode.org/canvas/code-components/packages Import React packages from esm.sh. Append '?external=react,react-dom' to avoid multiple React versions. ```javascript import { motion } from 'https://esm.sh/motion@12.23.26/react?external=react,react-dom'; ``` -------------------------------- ### Import Other Code Components Source: https://project.pages.drupalcode.org/canvas/code-components Import previously created components by prefixing their path with '@/components'. This allows for modularity and reuse of existing component logic. ```javascript import Heading from '@/components/my_heading'; ``` -------------------------------- ### Basic Image Component Usage Source: https://project.pages.drupalcode.org/canvas/sdc-components/image Include the canvas:image component with essential properties like src and alt. This is the simplest way to render an image. ```twig {% include 'canvas:image' with { src: 'https://example.com/image.jpg', alt: 'Example image', width: 600, height: 400, } only %} ``` -------------------------------- ### Define Link Prop (URI Reference Format) Source: https://project.pages.drupalcode.org/canvas/sdc-components/props Use for absolute or relative links. The 'format: uri-reference' allows full URIs or paths. ```yaml link: type: string format: uri-reference title: 'Link URL' examples: ['/about', 'https://example.com'] ``` -------------------------------- ### Workbench Configuration Source: https://project.pages.drupalcode.org/canvas/code-components/workbench Create a canvas.config.json file in your project root to customize Workbench's scanning paths and alias base directory. This is useful when your project deviates from the default Canvas layout. ```json { "componentDir": "./components", "pagesDir": "./pages", "aliasBaseDir": "src", "globalCssPath": "./src/components/global.css" } ``` -------------------------------- ### Define Image Object Prop Schema Source: https://project.pages.drupalcode.org/canvas/code-components/cli-tool/prop-schemas Use this schema to reference an image object. Only the file URL is required; other metadata like alt text, width, and height are optional. ```yaml type: object $ref: json-schema-definitions://canvas.module/image examples: - src: >- https://images.unsplash.com/photo-1484959014842-cd1d967a39cf?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1770&q=80 alt: Woman playing the violin width: 1770 height: 1180 ``` -------------------------------- ### Define Basic Text Input Prop Schema Source: https://project.pages.drupalcode.org/canvas/code-components/cli-tool/prop-schemas Use this schema for basic text input. It is stored as a string value. ```yaml type: string examples: - Hello, world! ``` -------------------------------- ### Define Video Object Prop Schema Source: https://project.pages.drupalcode.org/canvas/code-components/cli-tool/prop-schemas Use this schema to reference a video object. Only the file URL is required; other metadata like dimensions and poster are optional. ```yaml type: object $ref: json-schema-definitions://canvas.module/video examples: - src: https://media.istockphoto.com/id/1340051874/video/aerial-top-down-view-of-a-container-cargo-ship.mp4?s=mp4-640x640-is&k=20&c=5qPpYI7TOJiOYzKq9V2myBvUno6Fq2XM3ITPGFE8Cd8= poster: https://example.com/600x400.png ``` -------------------------------- ### Mock entry with props and slots Source: https://project.pages.drupalcode.org/canvas/code-components/workbench/mocks Defines a mock entry using 'props' and 'slots' to configure component props and map slots to element IDs. The 'elements' field defines the structure and content of child elements. ```json [ { "name": "Featured", "props": { "featured": true }, "slots": { "header": ["card-featured-header"], "content": ["card-featured-content"], "footer": ["card-featured-actions"] }, "elements": { "card-featured-header": { "type": "heading", "props": { "text": "Featured article" } }, "card-featured-content": { "type": "text", "props": { "content": "Learn how editorial teams use Canvas to review, refine, and publish AI-assisted content." } }, "card-featured-actions": { "type": "button-group", "props": { "stacked": false }, "slots": { "items": ["card-featured-primary-action", "card-featured-secondary-action"] } }, "card-featured-primary-action": { "type": "button", "props": { "label": "Get started", "href": "/sign-up", "variant": "primary" } }, "card-featured-secondary-action": { "type": "button", "props": { "label": "Read more", "href": "/articles/canvas-workflows" } } } } ] ``` -------------------------------- ### Image Component with onLoad Callback Source: https://project.pages.drupalcode.org/canvas/code-components/responsive-images Provide an `onLoad` callback function to execute code once the image has fully loaded and the placeholder is removed. The callback receives the event object. ```javascript {photo.alt} console.log(e.target.naturalWidth, e.target.naturalHeight)} /> ``` -------------------------------- ### Import and Use Image Component Source: https://project.pages.drupalcode.org/canvas/code-components/responsive-images Import the Image component from drupal-canvas and use it with dynamic photo props. Ensure all required props (src, alt, width, height) are provided. ```javascript import { Image } from 'drupal-canvas'; const MyComponent = ({ photo }) => { return ( {photo.alt} ); }; export default MyComponent; ``` -------------------------------- ### Build Navigation from Drupal Linkset Endpoint Source: https://project.pages.drupalcode.org/canvas/code-components/data-fetching Builds a navigation menu using Drupal core's linkset endpoint. Requires `drupal-canvas` and `swr`. Uses `sortLinksetMenu` to process the data. ```javascript import { sortLinksetMenu } from 'drupal-canvas'; import useSWR from 'swr'; const Navigation = () => { const { data, isLoading, error } = useSWR( '/system/menu/main/linkset', async (url) => { const response = await fetch(url); return response.json(); } ); if (error) return 'An error has occurred.'; if (isLoading) return 'Loading...'; const menu = sortLinksetMenu(data); return ( ); }; export default Navigation; ``` -------------------------------- ### Build Navigation from JSON:API Menu Items Source: https://project.pages.drupalcode.org/canvas/code-components/data-fetching Constructs a navigation menu from Drupal's JSON:API menu items. Requires `drupal-canvas` and `swr`. Uses `sortMenu` to order menu items. ```javascript import { JsonApiClient, sortMenu } from 'drupal-canvas'; import useSWR from 'swr'; const client = new JsonApiClient(); const Navigation = () => { const { data, isLoading, error } = useSWR( ['menu_items', 'main'], ([type, resourceId]) => client.getResource(type, resourceId) ); if (error) return 'An error has occurred.'; if (isLoading) return 'Loading...'; const menu = sortMenu(data); return ( ); }; export default Navigation; ``` -------------------------------- ### Image Component with Static Values Source: https://project.pages.drupalcode.org/canvas/code-components/responsive-images Use the Image component with static values for src, alt, width, and height. This is not recommended as it prevents responsive image variants from being generated. ```javascript Picture of the author ``` -------------------------------- ### Image Component with Eager Loading Source: https://project.pages.drupalcode.org/canvas/code-components/responsive-images Set the `loading` prop to 'eager' to force immediate loading of the image, overriding the default 'lazy' behavior. ```javascript {photo.alt} ``` -------------------------------- ### Image Component with Placeholder Prop Source: https://project.pages.drupalcode.org/canvas/code-components/responsive-images Set the `placeholder` prop to 'blur' to display a blurred image while the actual image loads, enhancing perceived performance. ```javascript {photo.alt} ``` -------------------------------- ### Define List of Integer Options Prop Schema Source: https://project.pages.drupalcode.org/canvas/code-components/cli-tool/prop-schemas Use this schema for a predefined list of integer options. The `meta:enum` property provides user-friendly labels for each enum value. Enum values cannot contain dots. ```yaml type: integer enum: - 1 - 2 - 3 meta:enum: 1: Option 1 2: Option 2 3: Option 3 examples: - 1 ``` -------------------------------- ### Define Formatted Text Input Prop Schema Source: https://project.pages.drupalcode.org/canvas/code-components/cli-tool/prop-schemas Use this schema for rich text content with HTML formatting support, displayed in a block context. Stored as a string. ```yaml type: string contentMediaType: text/html x-formatting-context: block examples: -

This is formatted text with HTML.

``` -------------------------------- ### Define List of Text Options Prop Schema Source: https://project.pages.drupalcode.org/canvas/code-components/cli-tool/prop-schemas Use this schema for a predefined list of text options. The `meta:enum` property provides user-friendly labels for each enum value. Enum values cannot contain dots. ```yaml type: string enum: - option1 - option2 - option3 meta:enum: option1: Option 1 option2: Option 2 option3: Option 3 examples: - option1 ``` -------------------------------- ### Define String Prop for Component Title Source: https://project.pages.drupalcode.org/canvas/sdc-components/props Use for basic text input for short text content. The schema defines the 'title' prop as a string. ```yaml title: type: string title: 'Component title' examples: ['Hello World'] ``` -------------------------------- ### Define String Prop for Rich Text Content Source: https://project.pages.drupalcode.org/canvas/sdc-components/props Use for rich text content with HTML markup. The schema defines 'content' with 'text/html' media type. ```yaml content: type: string title: 'Rich text content' contentMediaType: text/html x-formatting-context: block examples: ['

This is formatted text

'] ``` -------------------------------- ### Define Default Viewport Sizes Source: https://project.pages.drupalcode.org/canvas/apis/theme-settings Configure default viewport sizes for the Canvas editor preview. These settings are applied when a theme's `{theme}.canvas.yml` file is present. ```yaml viewports: mobile: 468 tablet: 1024 desktop: 1920 large_desktop: 2560 ``` -------------------------------- ### Define Date Prop for Date Picker Source: https://project.pages.drupalcode.org/canvas/sdc-components/props Use for date selection using a date picker input. The schema specifies 'format: date'. ```yaml event_date: type: string format: date title: 'Event date' examples: ['2024-12-25'] ``` -------------------------------- ### Define Image Object Prop Source: https://project.pages.drupalcode.org/canvas/sdc-components/props Use for image selection from the media library or uploads. The schema references a common image definition. ```yaml image: title: 'Component image' $ref: json-schema-definitions://canvas.module/image type: object examples: - src: '/path/to/image.jpg' alt: 'Component image' width: 800 height: 600 ``` -------------------------------- ### Define Integer Prop for Spacing Source: https://project.pages.drupalcode.org/canvas/sdc-components/props Use for numeric input, such as pixel values for spacing. Includes a minimum validation constraint. ```yaml spacing: type: integer title: 'Spacing in pixels' minimum: 0 examples: [20] ``` -------------------------------- ### Responsive Sizing with Sizes Attribute Source: https://project.pages.drupalcode.org/canvas/sdc-components/image Utilize the 'sizes' attribute to inform the browser about different image sizes based on viewport width. This optimizes image loading for various devices. ```twig {% include 'canvas:image' with image|merge({ sizes: '(max-width: 480px) 100vw, (max-width: 768px) 50vw, 33vw', loading: 'lazy', }) only %} ``` -------------------------------- ### CSS for Enhanced List Layout and Counter Source: https://project.pages.drupalcode.org/canvas/sdc-components/slots Styles for horizontal list layout with horizontal scrolling and item basis, and for displaying counters before each list item with specific formatting. ```css .list-direction-horizontal { flex-direction: row; overflow-x: auto; &>* { flex-shrink: 0; flex-basis: 300px; padding: 6px; } } .list-with-counter >* { counter-increment: list-number; &:before { font-size: 3rem; color: darkgrey; content: counter(list-number); padding-inline: .5rem; margin-inline-end: .5rem; } } ``` -------------------------------- ### Conditionally Construct Class Names with clsx Source: https://project.pages.drupalcode.org/canvas/code-components/packages Use the `clsx` utility for efficiently building CSS class strings, especially with conditional logic. ```javascript import { clsx } from 'clsx' export default function Example() { return (
// => 'foo bar baz' ); }; ``` -------------------------------- ### Fetch Nodes with JSON:API Source: https://project.pages.drupalcode.org/canvas/code-components/data-fetching Fetches articles using the JSON:API client and displays them with links. Requires `drupal-canvas` and `swr`. Uses `getNodePath` for URL generation. ```javascript import { getNodePath, JsonApiClient } from 'drupal-canvas'; import { DrupalJsonApiParams } from 'drupal-jsonapi-params'; import useSWR from 'swr'; const Articles = () => { const client = new JsonApiClient(); const { data, error, isLoading } = useSWR( [ 'node--article', { queryString: new DrupalJsonApiParams() .addSort('created', 'DESC') .getQueryString(), }, ], ([type, options]) => client.getCollection(type, options) ); if (error) return 'An error has occurred.'; if (isLoading) return 'Loading...'; return ( ); }; export default Articles; ``` -------------------------------- ### Image Component with Style Prop Source: https://project.pages.drupalcode.org/canvas/code-components/responsive-images Apply custom CSS styles to the underlying image element using the `style` prop. ```javascript {photo.alt} ``` -------------------------------- ### Override JSON:API client defaults Source: https://project.pages.drupalcode.org/canvas/code-components/data-fetching Instantiate the JsonApiClient with custom baseUrl and options to override default configurations like serializer or cache. ```javascript const client = new JsonApiClient('https://drupal-api-demo.party', { serializer: undefined, cache: undefined, }); ``` -------------------------------- ### Basic List Component CSS Styling Source: https://project.pages.drupalcode.org/canvas/sdc-components/slots Provides CSS for a vertical list layout with a maximum height and an auto-scrollbar for the items. Includes hover effect for list items. ```css .list-title { font-size: 1.3rem; font-weight: 700; margin-block-end: 1rem; } .list-items { display: flex; gap: .5rem; counter-reset: list-number; flex-direction: column; max-block-size: 300px; overflow-y: auto; & > *:hover { background-color: lightgrey;; } } ``` -------------------------------- ### Image Component with onError Callback Source: https://project.pages.drupalcode.org/canvas/code-components/responsive-images Implement an `onError` callback function to handle image loading failures. The callback is invoked if the image fails to load. ```javascript {photo.alt} console.error('Image failed to load')} /> ``` -------------------------------- ### Fetch collection with JSON:API Source: https://project.pages.drupalcode.org/canvas/code-components/data-fetching Fetch a collection of articles, including their tags, using the JSON:API client and DrupalJsonApiParams. Requires the JSON:API module to be enabled in Drupal. ```javascript import { JsonApiClient } from 'drupal-canvas'; import { DrupalJsonApiParams } from 'drupal-jsonapi-params'; import useSWR from 'swr'; const client = new JsonApiClient(); export default function List() { const { data, error, isLoading } = useSWR( [ 'node--article', { queryString: new DrupalJsonApiParams() .addInclude(['field_tags']) .getQueryString(), }, ], ([type, options]) => client.getCollection(type, options), ); if (error) return 'An error has occurred.'; if (isLoading) return 'Loading...'; return ( ); } ``` -------------------------------- ### Image Component with Additional Properties Source: https://project.pages.drupalcode.org/canvas/sdc-components/image Extend the basic image component usage by merging additional properties like loading, sizes, class, and custom attributes. Useful for fine-tuning responsive behavior and adding custom elements. ```twig {% include 'canvas:image' with image|merge({ loading: 'lazy', sizes: '(max-width: 768px) 100vw, 50vw', class: 'card--image', attributes: create_attribute({ 'data-testid': 'card-component-image', }), }) only %} ``` -------------------------------- ### Define Required Props Source: https://project.pages.drupalcode.org/canvas/sdc-components/validations List props that are mandatory for a component under the 'required' key. Saving the page will be blocked until these props are filled. ```yaml props: type: object required: - text text: type: string examples: - "Hot" ``` -------------------------------- ### Define List Component Metadata with Slots Source: https://project.pages.drupalcode.org/canvas/sdc-components/slots Defines a 'List' component with a 'title' prop and a 'content' slot for dynamic item insertion. The 'title' of the slot appears as placeholder text in Canvas. ```yaml $schema: https://git.drupalcode.org/project/drupal/-/raw/HEAD/core/assets/schemas/v1/metadata.schema.json name: List status: stable group: Music description: List component, displays sdc children as a vertical numbered list. props: type: object properties: title: type: string title: List title description: > Title of the list examples: ["My awesome list"] slots: content: title: List items description: > Place any SDC in this slot, to add them to the list. ``` -------------------------------- ### Image Component with Fill Prop Source: https://project.pages.drupalcode.org/canvas/code-components/responsive-images Utilize the `fill` prop to make the image fill its parent element, suitable for cases where dimensions are unknown. The parent must have a relative, fixed, or absolute position. ```javascript
{photo.alt}
``` -------------------------------- ### Enable @tailwindcss/typography Plugin Source: https://project.pages.drupalcode.org/canvas/code-components/packages Add the @plugin directive to your Global CSS to enable the typography plugin. ```css @plugin "@tailwindcss/typography"; ``` -------------------------------- ### Define Integer Prop Schema Source: https://project.pages.drupalcode.org/canvas/code-components/cli-tool/prop-schemas Use this schema for whole number values without decimal places. ```yaml type: integer examples: - 42 ``` -------------------------------- ### Use Image Object Prop in Twig Template Source: https://project.pages.drupalcode.org/canvas/sdc-components/props Includes a Twig template for rendering an image, passing the 'image' prop object. ```twig {% include 'canvas:image' with image only %} ``` -------------------------------- ### Define Enum Prop for Dropdown Source: https://project.pages.drupalcode.org/canvas/sdc-components/props Renders as a dropdown with predefined options. 'meta:enum' can customize display labels for each option. ```yaml alignment: type: string title: 'Text alignment' enum: ['left', 'center', 'right'] meta:enum: left: Left aligned center: Center aligned right: Right aligned examples: ['center'] ``` -------------------------------- ### Define Boolean Prop Schema Source: https://project.pages.drupalcode.org/canvas/code-components/cli-tool/prop-schemas Use this schema for true or false values. ```yaml type: boolean examples: - false ``` -------------------------------- ### Define Number Prop Schema Source: https://project.pages.drupalcode.org/canvas/code-components/cli-tool/prop-schemas Use this schema for numeric values that can include decimal places. ```yaml type: number examples: - 3.14 ``` -------------------------------- ### Singer SDC Component Metadata Source: https://project.pages.drupalcode.org/canvas/sdc-components Defines the metadata for the 'Singer' SDC component, including its name, group, description, and properties. ```yaml $schema: 'https://git.drupalcode.org/project/drupal/-/raw/HEAD/core/assets/schemas/v1/metadata.schema.json' name: Singer status: stable group: Music description: Singer component, could be used to display one singer. props: type: object required: - name properties: name: type: string title: Name description: Name of the singer. examples: ['Michael Jackson','Bruno Mars'] genre: type: string title: Genre description: Main music Genre of the singer enum: - Pop - Jazz - Soul - Funk - "Rock and Roll" - Electro examples: ['Pop'] image: $ref: json-schema-definitions://canvas.module/image type: object title: Image description: > Image of the singer examples: - src: 'micro.webp' alt: 'Nice picture of the singer' width: 200 height: 300 ``` -------------------------------- ### Define Textarea Prop for Long Text Source: https://project.pages.drupalcode.org/canvas/sdc-components/props Use for longer text content that spans multiple lines. The schema uses a $ref to a textarea definition. ```yaml description: type: string title: 'Description' $ref: json-schema-definitions://canvas.module/textarea examples: ['A longer description that spans multiple lines'] ``` -------------------------------- ### Define a Jumbotron Component with Slots Source: https://project.pages.drupalcode.org/canvas/code-components/slots This component uses slots for 'header' and 'buttonFooter' to allow flexible content insertion. Slot names are camelCased when passed as parameters. ```javascript const Jumbotron = ({ header, buttonFooter }) => { return (
{header}
{buttonFooter}
); }; export default Jumbotron; ``` -------------------------------- ### Responsive Prose Size Modifiers Source: https://project.pages.drupalcode.org/canvas/code-components/packages Combine 'prose' with responsive prefixes and size modifiers like 'md:prose-lg' and 'lg:prose-xl' to control font size across different screen sizes. ```html {body} ``` -------------------------------- ### Use Integer Prop in Twig Template Source: https://project.pages.drupalcode.org/canvas/sdc-components/props Applies top padding to a div using the 'spacing' integer prop, specifying units in pixels. ```twig
Content with spacing
``` -------------------------------- ### Use Date Prop in Twig Template Source: https://project.pages.drupalcode.org/canvas/sdc-components/props Renders a time element with the 'event_date' prop, formatting it to a human-readable date string. ```twig ``` -------------------------------- ### Eager Loading for Above-the-Fold Images Source: https://project.pages.drupalcode.org/canvas/sdc-components/image Override the default lazy loading behavior to 'eager' for critical above-the-fold images. This ensures important images load immediately. ```twig {% include 'canvas:image' with image|merge({ loading: 'eager', {# For hero images or critical content #} }) only %} ``` -------------------------------- ### Implement Integer Range Validation Source: https://project.pages.drupalcode.org/canvas/sdc-components/validations Add custom validations for props based on their types. For integer props, 'minimum' and 'maximum' can be used to define acceptable ranges. ```yaml test_integer_range_minimum: title: 'Integer, minimum=0' type: integer minimum: 0 ``` -------------------------------- ### Define Boolean Prop for Visibility Source: https://project.pages.drupalcode.org/canvas/sdc-components/props Use for true/false values, typically controlling the visibility of elements. The schema defines 'show_image' as a boolean. ```yaml show_image: type: boolean title: 'Show image' examples: [true] ``` -------------------------------- ### Fetch related articles excluding current Source: https://project.pages.drupalcode.org/canvas/code-components/data-fetching Fetch a list of articles, excluding the current one, using getPageData and a JSON:API client. Includes a wrapper component to safely handle cases where mainEntity might be null or not an article. ```javascript import { getPageData } from 'drupal-canvas'; import { JsonApiClient } from 'drupal-canvas'; import { DrupalJsonApiParams } from 'drupal-jsonapi-params'; import useSWR from 'swr' const client = new JsonApiClient(); function RelatedArticles({ mainEntity }) { const { bundle, entityTypeId, uuid } = mainEntity; const { data, error, isLoading } = useSWR( [ 'node--article', { queryString: new DrupalJsonApiParams() .addFilter('id', uuid, '<>') // Exclude current article by uuid. .getQueryString(), }, ], ([type, options]) => client.getCollection(type, options), ); return (...); } // Wrapper component to check for mainEntity existence and type before calling a hook since // hooks cannot be called conditionally (React rules of hooks). function RelatedArticlesWrapper() { const { mainEntity } = getPageData(); // Return early if there is no mainEntity, or it is not an article node. if ( !mainEntity || mainEntity.entityTypeId !== 'node' || mainEntity.bundle !== 'article' ) { return null; } return ; } export default RelatedArticlesWrapper; ``` -------------------------------- ### Enhanced List Component Props for Layout and Counter Source: https://project.pages.drupalcode.org/canvas/sdc-components/slots Adds 'direction' and 'with_counter' props to control list layout (vertical/horizontal) and display item counters. 'direction' accepts 'vertical' or 'horizontal'. ```yaml direction: type: string title: List direction enum: ['vertical','horizontal'] meta:enum: vertical: Vertical horizontal: horizontal examples: ['vertical'] with_counter: type: boolean title: Add counter description: Add a number to each list item examples: [false] ``` -------------------------------- ### Use Textarea Prop in Twig Template Source: https://project.pages.drupalcode.org/canvas/sdc-components/props Renders the 'description' prop within a div, using the 'nl2br' filter to convert newlines to HTML line breaks. ```twig
{{ description|nl2br }}
``` -------------------------------- ### Use Custom Tailwind CSS Color Class Source: https://project.pages.drupalcode.org/canvas/code-components/packages Apply custom theme colors defined via `@theme` directive as utility classes in your component markup. ```javascript export default function Example() { return
Drupal Blue
; } ``` -------------------------------- ### Dark Mode with Prose Invert Source: https://project.pages.drupalcode.org/canvas/code-components/packages Utilize the 'prose-invert' class to apply an inverted color palette for dark mode backgrounds. ```html {body} ``` -------------------------------- ### List Component Twig Template Source: https://project.pages.drupalcode.org/canvas/sdc-components/slots Renders a list component with a title and a content area for dynamically inserted items. Uses BEM methodology for CSS class naming. ```twig {# list.twig #}

{{ title }}

{{ content }}
``` -------------------------------- ### Defining Image Props in SDC Metadata Source: https://project.pages.drupalcode.org/canvas/sdc-components/image Define image properties within your SDC component's metadata schema. This allows the component to accept and pass image configurations, ensuring consistency. ```yaml $schema: https://git.drupalcode.org/project/drupal/-/raw/HEAD/core/assets/schemas/v1/metadata.schema.json name: Card props: type: object properties: image: $ref: json-schema-definitions://canvas.module/image type: object title: Card image examples: - src: https://placehold.co/400x300 alt: Card placeholder image width: 400 height: 300 ``` -------------------------------- ### Define Custom Viewport Sizes Source: https://project.pages.drupalcode.org/canvas/apis/theme-settings Set custom viewport sizes for specific breakpoints, such as mobile and desktop. If a viewport ID is missing or invalid, Canvas falls back to its default width. ```yaml viewports: mobile: 375 desktop: 1440 ``` -------------------------------- ### Customize Tailwind CSS Theme Variables Source: https://project.pages.drupalcode.org/canvas/code-components/packages Define custom theme variables, such as colors, using the `@theme` directive to extend Tailwind CSS. ```css @theme { --color-drupal-blue: #009cde; } ``` -------------------------------- ### Singer SDC CSS Styling Source: https://project.pages.drupalcode.org/canvas/sdc-components CSS styles for the 'Singer' SDC component, defining its layout and the appearance of its elements. ```css .singer { display: flex; align-items: center; gap: 0.5rem; } .singer-image { height: 50px; width: 50px; object-fit: cover; border-radius: 6px; } .singer-name { font-weight: bold; } .singer-genre { font-weight: normal; } ``` -------------------------------- ### Define Array Prop for Lists Source: https://project.pages.drupalcode.org/canvas/sdc-components/props Use for list inputs allowing multiple string values. Includes a maximum item count constraint. ```yaml numbers: type: array title: 'List of numbers' items: type: string maxItems: 10 examples: [['One', 'Two', 'Three']] ``` -------------------------------- ### Enhanced List Component Twig Template with Dynamic Classes Source: https://project.pages.drupalcode.org/canvas/sdc-components/slots Dynamically applies CSS classes based on 'direction' and 'with_counter' props to control list appearance. Renders the list title and items. ```twig {# list.twig #} {% set classes = [ direction ? 'list-direction-' ~ direction, with_counter ? 'list-with-counter' ]|join(' ') %}

{{ title }}

{{ content }}
``` -------------------------------- ### Remove Prose Max-Width Source: https://project.pages.drupalcode.org/canvas/code-components/packages Add the 'max-w-none' class to the 'prose' component to allow content to fill its container without a built-in max-width. ```html {body} ``` -------------------------------- ### Use Link Prop in Twig Template Source: https://project.pages.drupalcode.org/canvas/sdc-components/props Renders an anchor tag with the 'link' prop as the href attribute. ```twig Link text ``` -------------------------------- ### Use String Prop in Twig Template Source: https://project.pages.drupalcode.org/canvas/sdc-components/props Renders the 'title' prop as an H2 element in the Twig template. ```twig

{{ title }}

``` -------------------------------- ### Define Component Variants with class-variance-authority (cva) Source: https://project.pages.drupalcode.org/canvas/code-components/packages Utilize `cva` to define visual variants for components, managing class composition based on props like intent and size. ```javascript import { cva } from 'class-variance-authority'; const button = cva( 'font-semibold border rounded', // base classes { variants: { intent: { primary: 'bg-blue-500 text-white border-blue-500', secondary: 'bg-gray-200 text-gray-900 border-gray-200', }, size: { small: 'text-sm py-1 px-2', medium: 'text-base py-2 px-4', }, }, defaultVariants: { intent: 'primary', size: 'medium', }, }, ); // Usage button({ intent: 'secondary', size: 'small' }); // Returns: "font-semibold border rounded bg-gray-200 text-gray-900 border-gray-200 text-sm py-1 px-2" ```