Back to Lowdefy

@lowdefy/blocks-antd

code-docs/plugins/blocks/antd.md

5.2.013.6 KB
Original Source

@lowdefy/blocks-antd

Primary UI component library for Lowdefy, built on Ant Design v5 with CSS-in-JS (CSS variables mode). Contains 70 blocks covering most UI needs.

Overview

This is the default block package included with Lowdefy. It provides:

  • Form inputs (text, number, date, selectors)
  • Layout components (Card, Collapse, Tabs, Flex, Splitter)
  • Display components (Title, Paragraph, Alert, QRCode, Watermark)
  • Navigation (Menu, Breadcrumb, Pagination, FloatButton, Tour)
  • Feedback (Message, Modal, Progress)
  • Theming (ConfigProvider, ColorPicker, Segmented)

Styling Architecture

CSS Variables Mode

Ant Design runs in CSS variables mode (cssVar: { key: 'lowdefy' }, hashed: false) configured in the server's _app.js. The <Html> element has className="lowdefy" in _document.js so the CSS variables are set on :root, allowing Tailwind and custom CSS to reference --ant-* tokens. No Less or CSS-in-JS hashing at runtime.

withTheme HOC

Every block is wrapped with the withTheme(antdComponentName, BlockComponent) higher-order component (defined in src/blocks/withTheme.js). It intercepts properties.theme and, when present, wraps the block in a scoped <ConfigProvider> that applies per-block Ant Design token overrides:

javascript
// withTheme strips `theme` from properties and scopes a ConfigProvider
function withTheme(antdComponentName, BlockComponent) {
  const Wrapped = (props) => {
    const { theme, ...restProperties } = props.properties;
    const rendered = <BlockComponent {...props} properties={restProperties} />;
    if (!theme) return rendered;
    return (
      <ConfigProvider theme={{ components: { [antdComponentName]: theme } }}>
        {rendered}
      </ConfigProvider>
    );
  };
  // ...
}

// Usage in a block file:
export default withTheme('Badge', BadgeBlock);

The first argument (antdComponentName) must match the Ant Design component name so the theme tokens target the correct component.

cssKeys in Block Meta

Each block declares cssKeys in its meta, listing which style targets the block supports:

javascript
BadgeBlock.meta = {
  category: 'display',
  icons: [...],
  styles: [...],
  cssKeys: ['element', 'indicator'],
};

These keys define valid targets for the class (object form) and styles config properties. Common keys:

KeyPurpose
elementThe primary antd component element
popupPopup/dropdown overlays (selectors, date pickers)
indicatorStatus indicators (Badge)
headerHeader sections (Card, Collapse)

classNames and styles Prop Flow

The engine evaluates class and styles from the block config and exposes them as block.eval.class and block.eval.styles. The client rendering layer (Container.js, InputContainer.js, etc.) then passes these to the block component as classNames and styles props:

javascript
// In client/src/block/Container.js
<Component
  classNames={classNames} // resolved from block.eval.class
  styles={block.eval.styles} // e.g., { block: {...}, element: {...} }
  // ...
/>

The styles object maps cssKey names to inline style objects. The classNames object maps cssKey names to CSS class strings. Blocks use these to apply targeted styling to specific sub-elements.

Block Categories

Container Blocks

Layout and grouping components:

BlockPurpose
CardBordered container with header
CollapseAccordion panels
TabsTabbed content
ModalDialog overlay
DrawerSlide-out panel
PopoverFloating content
TooltipHover tooltip

Page Layout Blocks

Full page structure:

BlockPurpose
PageHeaderMenuPage with top navigation
PageSiderMenuPage with side navigation
LayoutFlexible layout container
HeaderPage header area
ContentMain content area
FooterPage footer area
SiderSidebar area

Input Blocks

Form input components:

BlockTypePurpose
TextInputStringSingle line text
TextAreaStringMulti-line text
PasswordInputStringPassword with visibility toggle
NumberInputNumberNumeric input with controls
SelectorSingleDropdown selection
MultipleSelectorArrayMulti-select dropdown
RadioSelectorSingleRadio button group
CheckboxSelectorArrayCheckbox group
ButtonSelectorSingle/ArrayButton-style selection
TreeSelectorSingle/ArrayHierarchical selection
DateSelectorDateDate picker
DateTimeSelectorDateDate and time picker
DateRangeSelectorArrayDate range picker
MonthSelectorDateMonth picker
WeekSelectorDateWeek picker
SwitchBooleanToggle switch
CheckboxSwitchBooleanCheckbox input
SliderNumberSlider input
RatingSliderNumberStar rating
AutoCompleteStringAutocomplete text
PhoneNumberInputStringPhone number with country code

Display Blocks

Content presentation:

BlockPurpose
ButtonClickable button
TitleHeading text (h1-h5)
TitleInputEditable heading text
ParagraphBody text
ParagraphInputEditable body text
LabelForm field labels
StatisticNumeric display with label
DescriptionsKey-value list
TagColored tag/badge
BadgeStatus indicator
AvatarUser avatar
ProgressProgress bar
ResultOperation result page
AlertAlert message box

List Blocks

Data display:

BlockPurpose
ControlledListRepeating block template
TimelineListTimeline display
CarouselImage/content carousel
PaginationPage navigation

App navigation:

BlockPurpose
MenuNavigation menu
MobileMenuMobile hamburger menu
BreadcrumbBreadcrumb trail
AffixSticky positioning

Feedback Blocks

User feedback:

BlockPurpose
MessageToast notification
NotificationRich notification
ConfirmModalConfirmation dialog

New in Ant Design v5/v6

BlockPurpose
FloatButtonFloating action button
TourStep-by-step guided tour
QRCodeQR code generator
ColorPickerColor selection input
SegmentedSegmented control (iOS-style toggle)
FlexFlexbox layout shorthand
SplitterResizable split panes
MasonryMasonry/waterfall grid layout
WatermarkBackground watermark overlay
ConfigProviderTheme/locale provider block

Special Blocks

BlockPurpose
DividerVisual separator

Note: The Comment block has been removed (the upstream Ant Design Comment component was removed in v5).

Common Properties

Most blocks support:

yaml
properties:
  # Per-block Ant Design token overrides (consumed by withTheme HOC)
  theme:
    colorPrimary: '#1677ff'

  # Content (varies by block)
  title: string
  content: string

  # State
  disabled: boolean
  loading: boolean

# Styling via class and styles (processed by normalizeClassAndStyles at build time)
class:
  block: 'p-4 rounded' # Tailwind classes on the layout wrapper
  element: 'text-primary' # Classes on the antd element
styles:
  block: { padding: 16 } # Inline style on the layout wrapper
  element: { color: 'red' } # Inline style on the antd element

Input Block Properties

All input blocks share:

yaml
properties:
  label:
    title: Field Label
    colon: true
    extra: Helper text
    span: 8 # Label width
  placeholder: Enter value
  disabled: false
  size: default # small, default, large

Selector Options

Selector blocks accept options:

yaml
properties:
  options:
    - label: Option A
      value: a
    - label: Option B
      value: b
      disabled: true

Or from requests:

yaml
properties:
  options:
    _request: getOptions

Page Layout Example

yaml
id: dashboard
type: PageSiderMenu
properties:
  title: Dashboard
  logo:
    src: /logo.png
areas:
  content:
    blocks:
      - id: stats
        type: Card
        properties:
          title: Statistics

Design Decisions

Why Wrap Ant Design?

Lowdefy wraps Ant Design to:

  • Provide consistent state binding
  • Add operator support in properties
  • Standardize event handling
  • Enable schema validation

Why So Many Selector Types?

Different selectors for different UX:

  • Selector: Standard dropdown
  • RadioSelector: When options visible at once
  • ButtonSelector: When options are actions
  • CheckboxSelector: Multi-select visible

Input Value Binding

Input blocks automatically:

  • Read from state[blockId]
  • Write to state[blockId] on change
  • Support value property override

E2E Testing Helpers

blocks-antd exports e2e helpers for Playwright testing via @lowdefy/e2e-utils.

Blocks with E2E Helpers

BlockActions (do.*)Assertions (expect.*)
Alert-visible, hidden, type, message
Buttonclickvisible, disabled, enabled, loading, text, type
Card-visible, hidden, title
Descriptions-visible, hidden, item
NumberInputfill, clearvisible, value, disabled
PageHeaderMenu-visible, title
Paragraph-visible, text
Result-visible, hidden, status, title
Selectorselect, clear, searchvisible, value, disabled, enabled
Statistic-visible, value, title
TextAreafill, clearvisible, value, disabled
TextInputfill, clearvisible, value, disabled

Helper Export Pattern

Each block's e2e.js file uses createBlockHelper:

javascript
// src/blocks/TextInput/e2e.js
import { createBlockHelper } from '@lowdefy/e2e-utils';
import { expect } from '@playwright/test';

const locator = (page, blockId) => page.locator(`#${blockId}_input`);

export default createBlockHelper({
  locator,
  do: {
    fill: (page, blockId, val) => locator(page, blockId).fill(val),
    clear: (page, blockId) => locator(page, blockId).clear(),
  },
  expect: {
    value: (page, blockId, val) => expect(locator(page, blockId)).toHaveValue(val),
  },
});

Common assertions (visible, hidden, disabled, enabled, validationError) are auto-provided by the factory.

Package Exports

json
{
  "exports": {
    "./e2e/TextInput": "./dist/blocks/TextInput/e2e.js",
    "./e2e/Button": "./dist/blocks/Button/e2e.js"
  }
}

Locator Patterns

Different blocks use different DOM locators:

BlockLocator Pattern
TextInput, NumberInput#${blockId}_input
Button#bl-${blockId} .ant-btn
Alert#bl-${blockId} .ant-alert
Selector#bl-${blockId} .ant-select

Note: Ant Design components don't always forward the id prop, hence the #bl-${blockId} wrapper pattern.

See Also