Component Library 2.7.3 (configured with Dummy App)

Previews

      • Button
      • Hyper Link
      • Icon
        • Email
        • Radio
        • Select
        • Telephone
        • Text
        • Textarea
      • Paginator
      • Article
        • Default
        • Hero Only
        • Multi Panel
      • Carousel
      • Fault Tolerance
      • Footer
      • Header
        • Background Hero With Breadcrumbs
        • Default
      • Pattern
        • Default
        • Error
        • Filled
        • No Legend
        • Broken Embed
        • Default
      • Image
      • List
      • Media Card
      • Text Card
  • Style Guide

No matching results.

Pages

  • Developer Guide
  • Overview
  • Colours
  • Typography
  • Spacing
  • New Components
  • Interactive Behaviour
  • Component Variants
  • CSS Utilities
  • Extending Components

No matching results.

Extending Components

Building Components From Your Own Data

This is the guide for consuming engines and apps — code that mounts the component library and wants to render its components from its own data, rather than from a BOP-fetched deserializer. (To add a new component to the library itself, see 05_adding_components.md.)

The data shape

Every component declares its data shape once, with data_attributes. That macro generates two things:

  • the delegate list the template reads from, and
  • a nested immutable value record, Component::Data (a Ruby Data), reachable as Component.data.

In production the component is handed a BOP deserializer. Downstream you build a Component::Data record instead. The component can't tell them apart — both satisfy the same duck-typed shape — so anything the library renders, you can render from your own data.

Recommended: .with(...).new(...)

Use the .with builder. It builds the data record and wires it into the component for you:

GllComponentLibrary::Patterns::TextCardComponent
.with(title: category.title, body: category.description, clickable: category_button(category))
.new

.with(**attrs) builds the ::Data record and returns the constructed component, injecting the record under the component's data kwarg — pattern: for patterns, panel: for panels:

GllComponentLibrary::Patterns::TextCardComponent.with(title: "Hi", body: "...")
# => TextCardComponent.new(pattern: TextCardComponent::Data.new(title: "Hi", body: "..."))

It works the same for panels, injecting panel::

GllComponentLibrary::Panels::HeroComponent.with(title: "Welcome", subtitle: "...")

Partial construction

Set only the fields you need; the rest default to nil. There's no need to pass every attribute.

GllComponentLibrary::Patterns::TextCardComponent.with(title: "Just a title")

Longhand equivalent

.with(...) is sugar for building the record by hand. These are identical:

# with the builder
TextCardComponent.with(title:, body:)
# longhand
TextCardComponent.new(pattern: TextCardComponent.data.new(title:, body:))

Reach for the longhand only when you also need to pass a constructor option (e.g. additional_class:) alongside the inline data — .with covers the data, the longhand lets you add the rest:

TextCardComponent.new(
pattern: TextCardComponent.data.new(title:, body:),
additional_class: "featured"
)

Components without .with

A couple of components (Patterns::ImageComponent, Patterns::VideoComponent) wrap their own input under a non-standard name — they declare data_attributes ..., to: :image / to: :video and set no data_source. They are normally rendered through a parent's asset slot, so .with is deliberately unavailable and raises NotImplementedError. Build their data with .data.new and pass it as pattern: directly:

GllComponentLibrary::Patterns::VideoComponent.new(
pattern: GllComponentLibrary::Patterns::VideoComponent.data.new(
autoplay?: true,
loop?: true,
desktop_video_url: source.desktop_video_url
)
)

Why this way

  • One source of truth — data_attributes names the shape once; you never hand-roll a struct to feed a component.
  • Interchangeable — inline ::Data records and BOP deserializers are the same duck type, so the component behaves identically whatever the source.
  • Only set what matters — partial construction keeps call sites to the fields you actually have.
CSS Utilities