npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

jia-design-to-code-react

v1.0.0

Published

A React-specific set of components and utilities to assist in creating web UI

Downloads

3

Readme

Design to Code React

The tooling available in Design to Code React can be used together to create UI for manipulating serializable data and viewing React components.

JavaScript   TypeScript

Benefits

Design to Code can be used in any combination for the following scenarios:

  • Mapping serializable data to a React component in an application
  • Editing data using a form generated from a JSON schema
  • Viewing a React component in an isolated iframe environment
  • Using a navigation generated from a components data
  • All of the above to create a live editing UI

Concepts

Ecosystem

The following components are intended to work together as an ecosystem of components:

Each of these components is provided as a standalone version and a version intended to work with another of the above components. If the Form is intended to be used with the Viewer then the Modular prefixed versions should be used. This enables them to share certain capabilities such as drag and drop.

Example:

import { DndProvider } from "react-dnd";
import HTML5Backend from "react-dnd-html5-backend";
import { ModularForm, ModularViewer } from "design-to-code-react";

// See details on implementation from the standalone
// versions of Form and Viewer
<DndProvider backend={HTML5Backend}>
    <ModularForm {...props} />
    <ModularViewer {...props} />
</DndProvider>

Installation

npm i --save design-to-code-react

Requirements

The design-to-code-react package will be installed with design-to-code. The design-to-code package includes exports required for implementing the React specific components, namely the MessageSystem and the minified webworker which handles data manipulation. Please refer to the documentation for design-to-code for a better understanding of these systems.

Form

The required property is the messageSystem, see design-to-code for details on setting this up.

Example:

import { Form } from "design-to-code-react";

/**
 * Add to your render function
 */
<Form
    messageSystem={messageSystem}
/>

Validation

Validation is treated as optional, there is a validation utility provided by the design-to-code package that will give basic JSON schema validation errors. Refer to the design-to-code README for details.

Drag and drop

Drag and drop is provided to the Form using the react-dnd package as well as the HTML5Backend. If you are using react-dnd somewhere else and need to implement the backend once, use the secondary export ModularForm.

Using form control plugins

All necessary form controls are built in by default but can be overriden either through the schema by adding a formControlId property with a string value or a control type defined below.

To make a custom control, use the secondary export StandardControlPlugin which will take care of all standard form actions such as setting default, resetting data, etc. You will need to provide the necessary functionality to the control as JSX.

When the plugin instance is passed to the <Form /> either the id or the type is then referenced and will cause the control to render.

A config is passed to the control, the specifications of this can be found here. Note that the ControlConfig interface may include extra properties depending on the control type being used.

Example id plugin:

JSON Schema:

{
    "type": "object",
    "properties": {
        "foo": {
            "type": "string",
            "formControlId": "foo"
        }
    }
}

JSX:

<Form
    messageSystem={messageSystem}
    controls={[
        new StandardControlPlugin({
            id: "foo",
            control: (config) => {
                return (
                    <input
                        value={config.value}
                    />
                )
            }
        })
    ]}
/>

Example type plugin:

<Form
    messageSystem={messageSystem}
    controls={[
        new StandardControlPlugin({
            type: ControlType.textarea,
            control: (config) => {
                return (
                    <input
                        value={config.value}
                    />
                )
            }
        })
    ]}
/>

List of control types

Control types are available as an enum provided as a secondary export ControlType and consist of the following:

import { ControlType } from "design-to-code-react";

// Available types
ControlType.select
ControlType.array
ControlType.checkbox
ControlType.linkedData
ControlType.numberField
ControlType.sectionLink
ControlType.section
ControlType.display
ControlType.button
ControlType.textarea

These control types can be paired with our default controls, the following of which are available:

  • SelectControl
  • ArrayControl
  • CheckboxControl
  • LinkedDataControl
  • NumberFieldControl
  • SectionLinkControl
  • SectionControl
  • DisplayControl
  • ButtonControl
  • TextareaControl

Note: If the id and type are not specified, all controls will be replaced with the control.

Example of a replacement type:

import { ControlType, TextareaControl } from "design-to-code-react";

...

<Form
    messageSystem={messageSystem}
    controls={[
        new StandardControlPlugin({
            type: ControlType.textarea,
            control: (config) => {
                return (
                    <React.Fragment>
                        <span>Hello world!</span>
                        <TextareaControl {...config} />
                    </React.Fragement>
                );
            }
        })
    ]}
/>

Example of a replacement for all controls, using the component for the default control:

<Form
    messageSystem={messageSystem}
    controls={[
        new StandardControlPlugin({
            control: (config) => {
                return (
                    <React.Fragment>
                        <span>Hello world!</span>
                        <config.component {...config} />
                    </React.Fragement>
                );
            }
        })
    ]}
/>

Making your own control plugin

The StandardControlPlugin creates a standard template for expected functionality not specific to a control such as a CheckboxControl. This may include showing a button to set the value to the default value, an unset/reset button if the value represented in the control is optional, etc.

It is possible to create your own control plugin template; this section is for more advanced usage and should be done with caution.

To assist in the creation of a custom control plugin template, another secondary export is provided, ControlTemplateUtilities. This is an abstract class that can be extended, it includes all of the render methods for various actions that can be taken that are not control specific. It is possible to use this class to make your own template and include extra logic for when these items should render.

Example:

import { ControlTemplateUtilities } from "design-to-code-react";

export class MyControlTemplate extends ControlTemplateUtilities {
    public render() {
        return (
            <div>
                <label
                    htmlFor={this.props.dataLocation}
                    title={this.props.labelTooltip}
                >
                    {this.props.label}
                </label>
                {this.renderConstValueIndicator("const-value-indicator-css-class")}
                {this.renderDefaultValueIndicator("default-value-indicator-css-class")}
                {this.renderBadge("badge-css-class")}
                {this.renderControl(this.props.control(this.getConfig()))}
                {this.renderSoftRemove("soft-remove-css-class")}
                {this.renderInvalidMessage("invalid-message-css-class")}
            </div>
        );
    }
}

export { StandardControlTemplate };

The following methods are available:

  • renderConstValueIndicator - This will indicate that this value is not the const value and will set the value to the const value if clicked.
  • renderDefaultValueIndicator - This will indicate that this value is not the default value and will set the value to the default if clicked.
  • renderBadge - This renders a badge, as indicated by the badge section of this README.
  • renderControl - This renders the control, such as CheckboxControl etc. or whatever control has been specified if the default controls are not being used. This must include an argument to execute the control with the getConfig method as an argument.
  • renderSoftRemove - This allows for the rendering of an unset/reset button if the value of this control is optional.
  • renderInvalidMessage - This method renders the invalid message for this control as specified when validating the data against the JSON schema.

Note that with the exception of renderControl method, the others require a string argument, this will be used as a class so that the generated HTML from the render method can be styled. At this point it is up to the implementer to include their own styling for these items.

It is recommended that the implementation also include the use of a label for accessibility.

Validation

Form validation uses the ajv package. The validation can be displayed inline or using the browser default HTML5 validation UI. This can be achieved through the displayValidationBrowserDefault which is true by default and displayValidationInline which will show validation messages below the associated form element.

JSON schema metadata

The schema form generator can interpret most JSON schemas, however there are some things to note when writing JSON schemas that make for a better UI.

Title

Using a title will add a label to the corresponding form element. All properties are required to have a title.

Example:

{
    "$schema": "http://json-schema.org/schema#",
    "id": "my-component",
    "title": "My component",
    "type": "object",
    "properties": {
        "text": {
            "title": "Text",
            "type": "string",
            "example": "Hello world"
        },
        "weight": {
            "title": "Weight",
            "type": "string",
            "enum": [
                "heavy"
            ]
        }
    },
    "required": [
        "text"
    ]
}

Description

Using a description will add a HTML attribute title to the label, resulting in a browser tooltip. This should be used for supplemental information that may not be apparent in the title.

Example:

{
    "$schema": "http://json-schema.org/schema#",
    "id": "my-component",
    "title": "My component",
    "type": "object",
    "properties": {
        "text": {
            "title": "Text",
            "description": "The text appearing in the body",
            "type": "string",
            "example": "Hello world"
        },
        "weight": {
            "title": "Weight",
            "description": "The weight of the text",
            "type": "string",
            "enum": [
                "heavy"
            ]
        }
    },
    "required": [
        "text"
    ]
}

Disabled

The disabled flag is optional and the form item representing this section of the schema will be disabled if flag is set to true.

Example:

{
    "$schema": "http://json-schema.org/schema#",
    "id": "my-component",
    "title": "My component",
    "type": "object",
    "properties": {
        "text": {
            "title": "Text",
            "type": "string",
            "example": "Hello world",
            "disabled": true
        }
    },
    "required": [
        "text"
    ]
}

Examples & default

Providing an examples or default value will replace the placeholder 'example text' or randomly generated number. It is generally better to add this extra information in case the schema form generator needs to create a new set of data.

Example:

{
    "$schema": "http://json-schema.org/schema#",
    "id": "my-component",
    "title": "My component",
    "type": "object",
    "properties": {
        "text": {
            "title": "Text",
            "type": "string",
            "examples": [
                "Hello world"
            ]
        },
        "style": {
            "title": "Style",
            "type": "object",
            "properties": {
                "color": {
                    "title": "HEX Color",
                    "type": "string",
                    "examples": [
                        "#FF0000"
                    ]
                }
            },
            "required": [
                "color"
            ]
        }
    },
    "required": [
        "text"
    ]
}

Because the style is optional, you can toggle to add it. The schema form generator will see that color is a required piece of data and use the example given to fill in.

Badges

To allow more detail about a field two additional fields can be added to JSON schemas, badge and badgeDescription. The badge can have the values "info", "warning" and "locked" which will create the related icons. Adding a badgeDescription will add a native tooltip when the badge is hovered.

Example:

{
    "type": "string",
    "badge": "warning",
    "badgeDescription": "Setting this field will cause adverse effects"
}

Alias

Occasionally the title provided by the JSON schema may not be enough information for the Form component, if an additional alias property is provided, this will be used as the linked data control label. In Chromium based browsers this will show both, and both the title text and alias text will autocomplete. In Firefox however, this will result in only showing the alias, so ensure that the alias text contains enough information to be easily autocompleted by a user.

Dictionaries

The additionalProperties JSON schema keyword can be used to create a dictionary of user-input keys on an object. To give these keys a label add the keyword propertyTitle, this will create a label for the form element for editing the property key.

Example:

{
    "type": "object",
    "additionalProperties": {
        "title": "A dictionary of strings",
        "propertyTitle": "A dictionary key",
        "type": "string"
    }
}

Categories

For improved UI when there become too many properties in an object, categories can be specified on a per-schema basis.

Example:

<Form
    messageSystem={messageSystem}
    categories={{
        "category-schema-id": {
            "": [
                {
                    title: "Style",
                    dataLocations: ["color", "outline", "font"],
                },
                {
                    title: "Content",
                    dataLocations: ["title", "body", "footer"],
                },
                {
                    title: "Advanced",
                    dataLocations: ["tracking", "accessibility"],
                    expandByDefault: false // default true
                },
            ],
        },
    }}
/>

This shows the root object of a schema with $id of category-schema-id that has the properties color, outline, font, title, body, footer, tracking and accessibility and splits them into categories with the appropriate titles.

The "Advanced" category has its expand default set to false, this means that initially it will be collapsed.

JSON schema keywords

Certain JSON schema keywords are interpreted to provide a better UI.

oneOf & anyOf

The oneOf and anyOf keywords can be used inside a property and at the root level of a schema. This will create a select dropdown so that the user can switch between them. If data has been provided, it will select the first oneOf/anyOf instance it can validate against. The contents of a 'title' property will be used for the contents of the dropdown.

Example:

{
    "$schema": "http://json-schema.org/schema#",
    "id": "my-component",
    "title": "My component",
    "oneOf": [
        {
            "title": "color",
            "type": "object",
            "properties": {
                "color": {
                    "title": "HEX Color",
                    "type": "string",
                    "example": "#FF0000"
                }
            }
        },
        {
            "title": "text",
            "type": "object",
            "properties": {
                "text": {
                    "title": "Text",
                    "type": "string",
                    "example": "Hello world"
                }
            }
        }
    ]
}

Enums

Any enums will be converted to a select dropdown.

{
    "$schema": "http://json-schema.org/schema#",
    "id": "my-component",
    "title": "My component",
    "type": "object",
    "properties": {
        "color": {
            "title": "Color",
            "type": "string",
            "enum" : [
                "red",
                "green",
                "blue",
                "yellow"
            ],
            "default": "red"
        }
    }
}

allOf & $ref

The allOf and $ref keywords cannot be interpreted by the schema form generator.

Categories

Any object in the <Form /> may have categories with which to contain its properties. This can be achieved by passing the categories prop which is a dictionary of keys that match to a schemas id, and which contain a dataLocation key to indicate which object a form category belongs to. Each category can then specify the properties as a set of dataLocation strings and a title.

Example:

<Form
    messageSystem={messageSystem}
    categories={{
        "https://my.schema.id": {
            "": [ // The root level dataLocation
                {
                    title: "Style",
                    dataLocations: ["border", "font"]
                },
                {
                    title: "Content",
                    dataLocations: ["text", "title"]
                }
            ]
        }
    }}
/>

Navigation

The required property is the messageSystem, see design-to-code for details on setting this up.

Example:

<Navigation
    messageSystem={messageSystem}
/>

Include data types

By default all JSON schema data types are visible. If the optional types prop is included, the data types that are in the array will become the data types that are rendered. If a data type that is specified is contained inside a data type that is not, that will directly nest the visible data type with the parent of the unspecified data type.

Example:

<Navigation
    messageSystem={messageSystem}
    types={["object", "string"]} // only "object" and "string" data types are rendered
/>

Viewer

The Viewer component creates an iframe, it can have a fixed or adjustable width and height and can be used independently or as a set with the SelectDevice and Rotate components.

The required property is the messageSystem, see design-to-code for details on setting this up and the iframeSrc.

Example:

<Viewer
    messageSystem={messageSystem}
    iframeSrc={"/example-content"}
/>

Setting width and height

The width and height can be set on the Viewer component which will be used as pixel values:

Example:

<Viewer
    messageSystem={messageSystem}
    iframeSrc={"/example-content"}
    width={500}
    height={300}
/>

To create a responsive width an height, the width and height can be tied to values in state and combined with the onUpdateHeight, onUpdateWidth and responsive props. This creates draggable borders around the iframe.

Example:

<Viewer
    messageSystem={messageSystem}
    iframeSrc={"/example-content"}
    width={this.state.viewerWidth}
    height={this.state.viewerHeight}
    responsive={true}
    onUpdateHeight={this.handleUpdateViewerHeight}
    onUpdateWidth={this.handleUpdateViewerWidth}
/>

// handlers for the `onUpdateHeight` and `onUpdateWidth` callbacks
handleUpdateViewerHeight = (newViewerHeight) => {
    this.setState({
        viewerHeight: newViewerHeight
    });
}

handleUpdateViewerWidth = (newViewerWidth) => {
    this.setState({
        viewerWidth: newViewerWidth
    });
}

Sending custom messages

Sending custom messages through from the iframe can be done with a postMessage to the iframe window. The custom message should define the type and action. The type should be MessageSystemType.custom imported from the design-to-code package and the action is defined as the enum value ViewerCustomAction.call provided as an export.

Example:

import { MessageSystemType } from "design-to-code";
import { ViewerCustomAction } from "design-to-code-react";

window.postMessage({
    type: MessageSystemType.custom,
    action: ViewerCustomAction.call,
    data: myData
}, "*");

Receiving custom messages

When a custom message is sent through the message system with a type of ViewerCustomAction.call, it will be passed to all registered callbacks with the message system using a modified action type of ViewerCustomAction.response. This way any further action that needs to be taken with the message data passed from the iframe can be done by looking for the response.

Select device

Use the SelectDevice component to select from provided default devices or provide your own custom device configurations. This component accepts a list of configured devices via the devices prop, some default devices are included with the package as a secondary export. It also accepts an activeDeviceId prop which maps to the current device id of the provided devices. In addition there is a callback onUpdateDevice which will fire a provided function with the new device id selected.

Example:

import {
    defaultDevices,
    SelectDevice,
} from "design-to-code-react";

<SelectDevice
    devices={defaultDevices}
    onUpdateDevice={this.handleDeviceUpdate}
    activeDeviceId={this.state.activeDevice.id}
/>

Devices

A device can be either "responsive" or "fixed", if it is responsive it does not take a width and height. The current active device can be used to activate the responsive prop on the Viewer component.

Example of custom devices passed to the devices prop:

[
    {
        "id": "responsive",
        "displayName": "Responsive display",
        "display": "responsive"
    },
    {
        "id": "phoneDevice",
        "displayName": "Phone device",
        "display": "fixed",
        "height": 800,
        "width": 320
    }
]

Rotate

Use the Rotate component to switch between landscape and portrait view. This component accepts an orientation prop which can be either "landscape" or "portrait". It also accepts an onUpdateOrientation callback which will fire a provided function with the new orientation selected.

Example:

import {
    Rotate,
} from "design-to-code-react";

<Rotate
    orientation={this.state.orientation}
    onUpdateOrientation={this.handleOrientationUpdate}
/>

Data utilities

Transforming data

As data from the dictionary of data is intended to be mapped to JSON schema, it may need to be transformed to be useful as, for instance, a React component.

Assuming that each JSON schema represents React props for a given component, a mapper has been provided which can be used in conjunction with the design-to-code export mapDataDictionary.

Example:

import { mapDataDictionary } from "design-to-code";
import { reactMapper } from "design-to-code-react";

const componentDictionary = {
    "button-schema-id": MyButton
}

const myComponentInstance = mapDataDictionary({
    dataDictionary: {
        foo: {
            schemaId: "button-schema-id",
            data: {
                children: "Hello world",
            },
        },
    },
    dataDictionaryKey: "foo",
    mapper: reactMapper(componentDictionary),
    schemaDictionary: {
        "button-schema-id": {
            id: "button-schema-id",
            type: "object",
            properties: {
                children: {
                    type: "string",
                },
            },
        },
    },
});

Expected result from the example above is an instance of MyButton with the text "Hello world". This is a simple mapper that assumes that any linked data is a React component that is nested.