Skip to content

Custom UI Layout #3503

Closed
Closed
@dcantu96

Description

@dcantu96

Prerequisites

What theme are you using?

other

Is your feature request related to a problem? Please describe.

Hello everyone, I'm opening this discussion because I had trouble figuring out a way to customize my form layouts. I wanted to demonstrate how I achieved this in code.

Describe the solution you'd like

this is my ui schema

const uiSchema: UiSchema<typeof schema> = {
  'ui:layout': {
    type: 'grid',
    cols: 2,
  },
  personalData: {
    'ui:layout': 'flex-col',
  },
}

this is my json schema

const schema: RJSFSchema = {
  type: 'object',
  title: 'Columns',
  properties: {
    name: {
      type: 'string',
      minLength: 3,
      description: 'Please enter your name',
    },
    vegetarian: {
      type: 'boolean',
    },
    birthDate: {
      type: 'string',
      format: 'date',
    },
    nationality: {
      type: 'string',
      enum: ['DE', 'IT', 'JP', 'US', 'RU', 'Other'],
    },
    personalData: {
      type: 'object',
      properties: {
        age: {
          type: 'integer',
          description: 'Please enter your age.',
        },
        height: {
          type: 'number',
        },
        drivingSkill: {
          type: 'number',
          maximum: 10,
          minimum: 1,
          default: 7,
        },
      },
      required: ['age', 'height'],
    },
    occupation: {
      type: 'string',
    },
    postalCode: {
      type: 'string',
      maxLength: 5,
    },
  },
  required: ['occupation', 'nationality'],
}

notice the "ui:layout"property on the uiSchema object. I used that property to map my ObjectFieldTemplate. So in the example, the first object will recieve the grid + 2 cols. and the personalData will recieve the flex-col. This way I conditionally render inside my custom ObjectFieldTemplate :)

kinda like this

export const ObjectFieldTemplate = (props: ObjectFieldTemplateProps) => {
  const maybeOptions = props.uiSchema?.['ui:layout'] as unknown
  if (maybeOptions === undefined || maybeOptions === null) return <DefaultFlexObjectFieldTemplate {...props} />
  else if (typeof maybeOptions === 'string') {
    const options = maybeOptions.split(' ')
    if (options.length > 1) {
      console.warn('ui:layout can only have one option, defaulting to flex-row')
      return <DefaultFlexObjectFieldTemplate {...props} />
    }

    switch (maybeOptions) {
      case 'grid':
        return <DefaultGridObjectFieldTemplate {...props} />
      case 'flex-col':
        return <DefaultFlexColObjectFieldTemplate {...props} />
      case 'flex-row':
      default:
        return <DefaultFlexObjectFieldTemplate {...props} />
    }
  } else if (typeof maybeOptions === 'object') {
    const { type, cols } = maybeOptions as { type?: unknown; cols?: unknown }
    if (type === 'grid') {
      if (typeof cols !== 'number') return <DefaultGridObjectFieldTemplate {...props} />
      else {
        if (cols > 5 || cols < 1) {
          console.warn('cols must be between 1 and 5, defaulting to 5')
        }
        const validCols = Math.min(Math.max(cols, 1), 5)
        return <DefaultGridObjectFieldTemplate {...props} cols={validCols} />
      }
    }
  }
  return <DefaultFlexObjectFieldTemplate {...props} />
}

Describe alternatives you've considered

I had to do this because I did not find a way to achieve this natively. I know the ui:className property is also there but I wanted to render my own custom components so that wasnt enough.

Metadata

Metadata

Labels

featureIs a feature request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions