question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Proposal: Conditional Rendering For Editor Properties Form

See original GitHub issue

Demand Overview

In Some situations, the component lib developers want to make the component property form whether to render depends on another property value.

Such as the Table Component, the buttonConfig property should only render when the column’s type is button.

Scheme Design

Maybe there are three ways to implement this demand:

  • Use Type.Union to define the properties spec
  • Add a new field to properties spec options to decide the property form whether should render
  • Let component lib developers use custom widgets to render the properties form

Type.Union

Currently, we already can use Type.Union to define property spec to make the form rendering depends on its value.

{
  "type": Type.Union(
    [
      Type.String({
        unionValue: 'text'
      }),
      Type.Object({
        buttonConfig: Type.Object({
          text: Type.String({
            title: 'Text',
          }),
          handlers: Type.Array(EventHandlerSchema),
        }, {
          title: 'Button Config',
          unionValue: 'button'
        }),
      }),
    ]
  ) 
}

But this spec would break the type checking because the TypeScript type of type in the above example would be:

type T = string | { buttonConfig: {...} };

So, Maybe this way doesn’t work?

New Field For Conditional Rendering

We can add a new field to spec options to describe when to render this property form. The type definition is:

interface Condition {
  // the key of the dependent property
  dependsOn: string;
  // rendering form when the dependent property value is equal to this value
  value: string;
}

type UnionConditions = Condition[];
type IntersectionConditions = Condition[];

And the example specs are just like these:

// render when: type === 'text' || type === 'button'
{
  "text": Type.String({
    "unionConditions": [{
      "dependsOn": "type",
      "value": "text"
    }, {
      "dependsOn": "type",
      "value": "button"
    }]
  })
}
// render when: type === 'text' && enable === true
{
  "text": Type.String({
    "intersectionConditions": [{
      "dependsOn": "type",
      "value": "text"
    }, {
      "dependsOn": "enable",
      "value": true
    }]
  })
}

But, how to design the dependsOn is a question, we should split it into different situations to talk about it.

The Related Properties Are Under The Same Level

When the related properties are under the same level, it would be simple to design the properties spec. The dependsOn is set to the key of another property which is under the same level just alright. Such as:

{
  "columns": Type.Array(
  	Type.Object({
      ...,
      "type": Type.KeyOf(...),
      "buttonConfig": Type.Object({...}, {
        "unionConditions": [{
          "dependsOn": "type",
          "value": "button"
        }]
      })
    })
  )
}

The Related Properties Are Cross The Different Levels

We can set the absolute path to describe which property to depend on. Just like:

{
  "columns": Type.Array(
  	Type.Object({
      ...,
      "type": Type.KeyOf(...),
      "buttonConfig": Type.Object({...}, {
        "unionConditions": [{
          "dependsOn": "columns.type",
          "value": "button"
        }]
      })
    })
  )
}

But the above spec has a problem here. You may notice that if a property is Array type, it would have the same sub-properties key between different array items, but which property’s value should we actually depends on?

I think we should define some rules for the Array type properties when depending on it.

  • Only allow depending on the property which in the same array item
  • Not allow depending on the property under the other Array type properties

Custom Widget

We will support registering the custom widgets for properties form later, and then the component lib developers can freely implement the form widgets that they want.

However, if the related properties for conditional rendering aren’t under the same level, the component lib developers have to implement a large widget to achieve this.

In addition, if a component lib has many properties which only need to render the form depending on different conditions, they have to implement many different widgets for them. That is awful for the component lib, so we better support the common way for the condition rendering.

Summary

There are three ways that we mention above to implement conditional rendering for properties form, let’s take a summary for them.

image

In conclusion, I think the second scheme is better.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:1
  • Comments:10 (9 by maintainers)

github_iconTop GitHub Comments

1reaction
MrWindlikecommented, Feb 24, 2022

Flexible. Should we consider other logic operators like OR? Should we consider other cooperation operators like IN, GREATER_THAN, SOME, EVERY?

Yes, it’s flexible. We can support more operators incrementally in the future and don’t worry to make a breaking change. If we want to add more operators, we can add the operators just like this:

type BaseType = string | number | boolean;
// comparison
type EQ = { 
    // rendering form when the dependent property value is equal to this value
    value: BaseType;
};
type LT = { lt: number };
type GT = { gt: number };
type In = { in: BaseType[] };
type ComparisonOperator = EQ | LT | GT | In;
// logic
type Or = { or: (Logic | ComparisonOperator)[]; }
type And = { and: (Logic | ComparisonOperator)[]; }
type Logic = Or | And;

type Condition = {
  // the key of the dependent property
  dependsOn: string;
} & (ComparisonOperator | Logic);

type UnionConditions = Condition[];
type IntersectionConditions = Condition[];

Easy to implement, for component developers and the editor runtime.

Component developers, only need to follow the design to write JSON just can implement the conditional rendering. In addition, we also export the TypeScript type definition to help the component developers to write the conditional JSON.

The editor needs to implement a function to parse the conditional JSON to real logic execution and another function to get the real value by the path.

Robustness. What will the editor do once a component defined an invalid union condition?

Maybe we should return the false condition and hide the form widget when the conditional JSONs are invalid?

1reaction
Yuyz0112commented, Feb 23, 2022

Oh, looks like the JSON schema also have a conditional semantic built-in: if-then-else

Read more comments on GitHub >

github_iconTop Results From Across the Web

Conditional Rendering of a Form Based on Case Status LWC ...
It looks like it is missing a equals on if:true if:true={isClosed}> (and as glls mentioned you should create a property). Try that, but ......
Read more >
React conditional rendering: 9 methods with examples
In React, conditional rendering refers to the process of delivering elements and components based on certain conditions. There's more than one ...
Read more >
4 Methods to Add Conditional Attributes to React Components
So, in this article, I will discuss different methods and best practices of using conditional attributes or props with React. Rendering ...
Read more >
7 Ways to Implement Conditional Rendering in React ...
This article covers seven different ways to implement conditional rendering in React applications with practical examples and performance ...
Read more >
Custom Property Drawers, conditional rendering of fields in a ...
I wanted to use Custom Property Drawer not full blown custom editor, so that I only would need drawing of AttributesLevels. I could...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found