What is the properties panel

A property panel, as its name suggests, is an ‘object’ where properties can be displayed and manipulated. So what are attributes? Wikipedia explains:

Attribute is the abstract description of an object, which represents the properties and characteristics of the object

This description is too abstract and academic. Let’s combine specific cases to see what specific practices different fields have for this’ object ‘used for attribute display and manipulation

The invention of electricity has brought great progress to the whole industrial field. At the same time, the switch (switch) controlling the power supply helps us to control the opening or closing of electricity. This is the “object” to display and operate the property of the opening or closing of electricity — the property panel

The existence of the properties panel helps us better display the state of things and perform actions to change their state

For example, we inevitably come into contact with cars every day to and from work. Various attributes on the dashboard reflect the state of the car itself, which can help us better understand the speed of the car. How much oil is left? Is the water temperature too high? Is the system normal? Behind because we need to focus on various road conditions, whether to have overtaking, left and right side if there is a pedestrian, is not likely to take out the energy of the state of the car itself to carry on the detailed understanding, again through the dashboard the properties panel, we don’t need to understand the car itself complicated knowledge, such as well as the elevator button, keyboard, etc

Property panels play an important role in the industry with a long history of development. Looking to the present, in recent years, the rapid development of computer software field has also appeared a variety of property panels, such as the ancient front-end visualization development tool — Dreamweaver, the property panel under the software can make it easier for developers to adjust the content and properties of the current operation module

In the field of design, sketch and FigMA both use the property panel, through which designers can quickly control the elements on the canvas, greatly improving the efficiency of design

Build the domain’s property panel

As mentioned above, in different fields, “property panel as the carrier of operation properties can help us shield those complex underlying knowledge” has been practiced to a certain extent. Then how to draw lessons from this idea in the construction field?

In the “Web Component” design pattern, each page with multiple interactions is composed of components, each of which has defined properties that reflect the various states of the current component and are very complex. The use of a property panel makes it easier to display and edit components, so what capabilities should such a property panel have?

For components, panels should be documentation and self-describing, i.e. developers can understand what a component’s properties are and how they should be used and edited just by looking at the text, description, and corresponding action areas on the properties panel

What problems need to be solved

For basic purposes, a property panel can clearly describe at least the following basic questions

  • Label: “Attribute name”, which directly tells the developer what the attribute is used for, such as the button’s Type attribute
  • Description: “Description of the attribute”. When the attribute name is not intended to be recognizable at a glance, the attribute can be used for detailed description, such as a copy describing type, or even some demo
  • Content: “Attribute renderer”, which allows users to modify attributes, most commonly by input, Textarea, Select, buttonGroup
  • Error: “attribute verification information”, which is displayed when an invalid attribute data is entered, such as a number instead of a string for type

The nature of the property panel

With the basic principles described above, we can see that the most appropriate way to carry this content is through a form form, where each property corresponds to a formItem. Think of the scenario we fill out every time we fill out a form

Each line contains the column items we mentioned above (description is not marked here because attribute names are easy to understand).

  • Explain what this formItem is for by label
  • Tell the user what to do with the property by rendering the content
  • If the modified data does not meet the attribute requirements, an error message is displayed

At this point, we will find that the nature of the properties panel is actually a form, how to design and implement such a form is crucial

Design and implementation

Before we get to the implementation, the first question is what data structure do we use to present each of the properties of the above comb? JSON (JavaScript Object Notation) is a simple and readable SYNTAX for describing JavaScript objects. It is the most commonly used data exchange format for Internet services

Based on this concept, JSON Schema emerged, which is a description of JSON data structure. It not only conforms to JSON format, but also can be used to verify JSON data, which is very convenient (see JSON Schema website for more information).

{
  "title": "Product"."description": "A product in the catalog"."type": "number"
}
Copy the code

Match the data with the most appropriate renderer

For “label”, “description”, “error” and so on, it is not complicated to design and implement. Let’s focus on the attribute renderer, which provides the user with the operation

It is necessary to use an appropriate renderer to render the schema, such as the following type declarations

/** * @title Select item */
size: "small" | "middle" | "large";
Copy the code

Size has only three values, semantics similar to enumeration, suitable for editing with drop-down selection

export const select = { renderType: 'select', Component: SelectRender, tester: (schema) => schema.enumOptions? .length >2,
} as RenderRegisterCommand;
Copy the code

We can get a relatively complete rendering rule by precipitation of these inferred rules for meta information. However, some schemas can have multiple appropriate renderers for rendering. For example, data of number type can be rendered by inputNumber

export const inputNumber = {
  renderType: 'number'.Component: InputNumberRender,
  tester: (schema) = > schema.type === 'number',}as RenderRegisterCommand;
Copy the code

You can also render using sliders

export const slider = {
  renderType: 'slider',
  Component: SliderRender,
  tester: (schema) => schema.type === 'number',
} as RenderRegisterCommand;
Copy the code

How do you solve scenarios where you might have multiple renderers that are appropriate? We’ll focus on this later in this article, but first we’ll look at several important parts of each RenderRegisterCommand

  • RenderType: The unique identifier of the renderer
  • Component: The Component used for rendering
  • Tester: The tester-validated schema allows you to select the corresponding Comonent for rendering

As we all know, there are seven basic data types in JavaScript, including Number, String, Boolean, Symbol, undefined, NULL, and Object

  • Symbol is only intended to provide a unique value, and it is perfectly possible to mask this type on low-code platforms, reducing user costs
  • For undefined, JSON itself does not support this data type

There are only five data types to consider at this point. Let’s look at how the most common types of number, string, and Boolean can be rendered

number

Renderer type
inputNumber
slider

string

Renderer type
input
textArea
color

boolean

Renderer type
checkbox
switch

How should the other two complex data types be handled and presented?

Ability to handle parent-child rendering levels

In the JavaScript world, there is often a saying that everything is an object, which shows the importance of this data type. Object is also a complex type in design, with many interaction details worth considering, for example

  • How do we distinguish between properties that belong to other properties, that is, parent-child relationships between properties
  • How can we solve the problem of an object with too many properties taking up too much space

Here is a simple design idea

Use different background colors or indent to distinguish between the parent and child, and provide folding to control the problem of too many attributes.

If a child property is also an object, that is, an object nested within an object, how should we present the objects in the child property?

In this case, we use array as a data structure in which the elements are objects, because in JavaScript, array is an object, which naturally overwrites the scene of objects nested within objects, and we need to express and manipulate arrays in a more intuitive and convenient way, for example

  • You can move each element of the array
  • You can easily add and delete elements in an array

You can render nested complex structures by dragging and dropping the order of each item, and then popover to show the properties of specific child objects

A property that can be closed

The next step is to handle the null data type. Although it is not complicated, there are some unclear problems when describing and modifying the data type through the renderer. For example, how to express a value is null or the string ‘null’.

From current usage scenarios, there is very little intent to use NULL directly. Therefore, we can move to nullable design with closed properties, which matches well with the way component properties are used today

The paging property of the Table component, for example, can be switched to an object to configure detailed paging data, or it can be switched entirely to false to express no paging

Table.pagination: false | Pagination
Copy the code

By adding a checkbox in front of the label to say “If I turn on this attribute, I pass it to the component, otherwise I don’t.”

Extensible renderer design

That’s where the basic design comes in, and then there’s the big extension issue. How can we provide a mechanism to display custom renderers when the default attribute renderer does not meet user needs?

export class Factory {
 	private static renderMap: Record<string, RenderRegisterCommand> = {};

	static register(commands: RenderRegisterCommand[]) {
    commands.forEach((command) = > {
      this.renderMap[command.renderType ?? command.type] = command;
    });
  }
}

Factory.register([inputNumber, slider]);
Copy the code

By means of factory registration

Users can implement custom renderers in the format of the original RenderRegisterCommand convention, and then add or override them by calling the register function exposed in the Factory Factory

Verify the input data

So at this point, we have the schema and the renderer that renders the schema, and we can manipulate those renderers to generate the data that we want, and then what happens? Yes, “validation” becomes an important means of ensuring that the data format is legitimate

{
  title: "Todo",
  type: "object",
  properties: {
    title: {
      title: "Title",
      type: "string",
    },
    done: {
      title: "Done?",
      type: "boolean",
    },
  },
  required: ["title"],}Copy the code

The JSON Schema specification itself clearly describes a set of validation rules. When used with ajV, it is very easy to determine whether the current data is valid and to obtain specific error messages

try {
  const result = ajv.validate(schema, value);
  if (result) {
    return null;
  }
  return ajv.errors
} catch (e) {
  console.error('[Ajv validator]:', e.message);
}
Copy the code

Extensible data verification

When it comes to ajV validation data types, perhaps we realize that most operations are now based on JavaScript primitive data types. How do we provide the ability for users to expand when they want to customize some non-javascript primitive data types?

Factory.registerDataType([ { type: 'moment', validate: (value) = > {the if (! Value. Name. Includes?. (" w ")) {return {message: 'name attribute must contain the letters w'};}},},]);Copy the code

Similar to the “extensible renderer design” above, the values generated by the current renderer are passed to the user to do some custom judgment logic and return a trusted error message

const { message: errMsg } = dataTypeMap? .validate? .(data) || {};Copy the code

After that, the user – defined verification logic can be invoked at the verification place

Customize other action points

At this point, a formalized properties panel is ready to use, which not only automatically selects the appropriate renderer, but also verifies the validity of the input data

But more than that, in “Match the right renderer for the data”, we were left with an important question: what if there are multiple renderers that can be used as renderers?

At the end of the renderer symbol and in the panel that pops up, we list all the appropriate renderers that have been matched, making it easy for the user to choose the rendering method they want to use

In addition, if each piece of data wants to be able to customize the action like switching, for example, to report this piece of data to a platform, jump to a link, what should be done?

Factory.registerAction({
  Component: ({ schema, value }) = > {
    console.log('schema', schema, value);
    if (schema.type === 'array') {
      return <a style={{ fontSize: 12.position: 'absolute', top: 2.left: - 80.}} >configuration</a>; }}});Copy the code

The schema and value of the current renderer are passed to the user, who returns to the ReactNode for judgment and processing, and mounts the returned node at the appropriate location

future

At this point, the design of the property panel comes to an end. Here, the core points that need to be paid attention to in the design are briefly combed

Cover more application scenarios

As mentioned in the article, the nature of the properties panel is actually a form. How can this form be adapted to enough scenarios?

  • Some scenarios want labels and renderers to be horizontal; There is hope for a vertical layout
  • Some scenes want the whole thing to be dark; And some hope is white style
  • If there is not enough space in the scene, I hope the properties panel can be compact. And some hope to be a little bit bigger and looser

What we’ll see is that what we’re really doing is creating a form with a theme, which, with different themes, has different abilities to cover as many scenarios as possible

A system of extremely open plug-ins

Covering enough scenes is only the first step for the property panel to go out. In the face of different business needs of different users, how to let them access their desired capabilities at the lowest cost is the core embodiment of the vitality of the property panel

Highly maneuverable renderer

The renderer design above is a stage product. In addition to the most basic string, number and Boolean rendering methods, other interactive methods are also facing some challenges. The design in this paper is biased to the perspective of front-end engineers, and specific data or indicators are still needed to prove that such a design is reasonable. How to make them more accessible and universal need to be polished by some quantitative means