Adjusting Components

Mark components by comment

Component properties are serialized based on the default export of class/function. If the export of a component is not a function/class that exposes its own props but is wrapped by another function, then the serialization is not possible.

export default withAppProvider<Props>()(Button);

In the Button component from the example above we cannot serialize properties (and finally can't display the Properties panel). To handle this approach, you have to add a JSDoc comment to handle this component.

A solution to the case above is to add a JSDoc comment @uxpincomponent before the class/function that declares the component.

/**
 * @uxpincomponent
 */
export class Button extends React.PureComponent {
  // ...
}

You have to add a @uxpincomponent comment before the class/function.

Namespaced Components

You may use the nested components approach in your library. It means that your component contains another component. Use them as represented below:

import Autocomplete from '../Autocomplete';
 
const textField = (
  <Autocomplete.ComboBox.TextField
    onChange={this.updateText}
    label="Tags"
    value={this.state.inputText}
    placeholder="Vintage, cotton, summer"
  />
);
 
export default (
  <Autocomplete
    allowMultiple
    textField={textField}
    listTitle="Suggested Tags"
  />
);

There are 3 levels of components here:

  • top-level component: Autocomplete
  • second-level component: ComboBox
  • third-level component: TextField

To handle this approach you have to declare the namespace in your component.

Declare namespaced components

To declare the namespace in components, use the JSDoc comment @uxpinnamespace, and declare the name of the parent component. Looking back at our example, you need to declare the namespaces in ComboBox and TextField components like below:

ComboBox.jsx

/**
 * @uxpinnamespace Autocomplete
 */
export class ComboBox extends React.PureComponent {
  // ...
}

TextField.jsx

/**
 * @uxpinnamespace Autocomplete.ComboBox
 */
export class TextField extends React.PureComponent {
  // ...
}

On our example, the namespace for the ComboBox component is Autocomplete because it's a direct child of the Autocomplete component. And the namespace for the TextField component is Autocomplete.ComboBox because it’s on the 3rd level from the top component (1st level), and its parent is the ComboBox. This is how we support multilevel nesting.

Use namespaced components in preset

After adding the @uxpinnamespace comment, you'll be able to use components in merge presets. You can do this the same way as in your application code.

When you import the Autocomplete component in preset by:

import Autocomplete from '../Autocomplete';

You'll be able to use components to define preset like in your code:

import Autocomplete from '../Autocomplete';
 
const textField = (
  <Autocomplete.ComboBox.TextField
    onChange={this.updateText}
    label="Tags"
    value={this.state.inputText}
    placeholder="Vintage, cotton, summer"
  />
);
 
export default (
  <Autocomplete
    allowMultiple
    textField={textField}
    listTitle="Suggested Tags"
  />
);

You can also import the TextField component directly like below:

import TextFiled from '../components/ComboBox/components/TextField';

Then you will create a preset like this:

import Autocomplete from '../Autocomplete';
import TextFiled from '../components/ComboBox/components/TextField';
 
const textField = (
  <TextField
    onChange={this.updateText}
    label="Tags"
    value={this.state.inputText}
    placeholder="Vintage, cotton, summer"
  />
);
 
export default (
  <Autocomplete
    allowMultiple
    textField={textField}
    listTitle="Suggested Tags"
  />
);

Use namespaced components in JSX property

The components declared in this way can be used in the properties panel as a value in node/element properties:

Namespaced components in Library

Namespaced components in the library panel would display with a full namespace name:

Resizing Components

The presentation of code components in UXPin may require adjustments at times. That is especially important for the size of your code components.

Sometimes the bounding box of the code component may be larger than the element itself (skip container).

In other cases, you may want to block the possibility to resize the component (non-resizable). Or you may require to cover both at the same time.

Use Cases

Skip container

In some cases, the bounding box of the code component in UXPin can not match the real size of the element. To solve this problem you can point a specific HTML node (by default Merge uses root node of the component).

uxpinRef

This is a specific property that is passed to the component along with regular props. It must be set to React ref attribute, for example:

/**
 * @uxpinwrappers
 * SkipContainerWrapper
 */
const Button = ({greeting='Hello', uxpinRef}) => (
  <div>
    <button className="btn" ref={uxpinRef}>{greeting} 👋</button>
  </div>
);

Non-resizable component

Some code components aren’t resizable and you can mark this component as non-resizable to Merge. Thanks to this action we won’t see transformers to resize component in UXPin editor.

/**
 * @uxpinwrappers
 * NonResizableWrapper
 */
const Button = ({greeting='Hello}) => (
  <div>
    <button className="btn">{greeting} 👋</button>
  </div>
);

Combined cases

You can use both of the actions (and more in the future) for the same component.

/**
 * @uxpinwrappers
 * SkipContainerWrapper, NonResizableWrapper
 */

Interaction triggered on property change

In many cases, the designer wants to get value from the prototype to update the content of the prototype. It means there needs to be a way to bind, store, and use values between components and UXPin.

@uxpinbind allows you to bind the interaction to a specific property of the component.
In the editor, all properties that have a defined binding are available as interaction triggers for the selected code component.

It allows the designer to:

  • Trigger interactions based on the user's input
  • Get the value user provided and store it as property
  • Create conditional interactions

Example of bound interaction:

Checkbox.propTypes = {
  onChange: PropTypes.func,
  /**
   * @uxpinbind onChange 0.target.checked
   */
  isChecked: PropTypes.bool,
}

Example

Replace content based on the active tab using a Tabs component

In Material UI (repository available here), there is a component named Tabs. It basically allows creating navigation based on tabs. This component contains other components as children - Tab.
As a designer, we may want to create an interaction to present specific content, let's say Card based on the selected Tab. UXPin won't be able by default to get the value of the selected tab from Tabs component. To do it, we'll use @uxpinbind

The first step is to check the documentation of the component.
In this example, we'll bind two properties - onChange and value. The goal is to set an interaction in UXPin that will trigger interaction onChange once the value will be changed.

The next step is to add @uxpinbind annotation to the value that will trigger interaction. @uxpinbind requires specific information about the parameter to bind the interaction.
For Tabs we want to use a value of the property and that's why we use the second parameter of the property on Change 1

Once the @uxpinbind is defined designer can create an interaction using value as a trigger for the interaction onChange.