/yupiik-preact

A simple preact libraries set targetting configured UI (from static files or even a backend).

Primary LanguageJavaScript

@yupiik/preact

Simple library enabling to render preact components from a registry + config and some utilities around JSON-RPC as a hook. Overall goal is to configure its UI.
Important
this library is not yet deployed on npm so you will have to install it locally yourself for now.

Dynamic component Usage (@yupiik/dynamic)

import { render } from 'preact';
import { Dynamic } from '@yupiik/dynamic';

const registry = {
    component1: Component1,
    component2: Component2,
    component3: Component3,
}

render(
    <Dynamic
        registry={registry}
        options={{
            name: 'component1',
            options: {
                // whatever options component1 supports
            }
        }}
    />,
    document.getElementById('app'));

FromConfiguratonHoc

FromConfiguratonHoc is a companion for Dynamic (a HoC actually on top of Dynamic) which is purely configuration oriented. It handles callbacks, children etc from plain JSON.

Configuration

Configuration schema (properties of the component):

{
    registry = undefined,
    parentState = undefined,
    parentDispatch = undefined,
    options: {
        name,
        options: {
            useReducerCallback: {
                initialState = undefined,
                reducerJsonLogic = false,
            } = {},
            useEffectCallback = [],
            callbacks = {},
            configuration = {},
        } = {},
    },
}
Property Description

registry

component registry (key matching the component name)

name

the component name in the registry

useEffectCallback

the useEffect callbacks properties definition in JSON-Logic format, it can be an array or an object (behaves as an array of 1 element). Each element is a JSON-Logic expression.

useReducerCallback

the useReducer callbacks. initialState is the state initial value and reducerJsonLogic the JSON-Logic implementation which takes the current state and action as parameters. JSONLogic take the state and dispatch functions as parameters.

callbacks

the callbacks properties definition in JSON-Logic format, key is the callback name and value its JSON-Logic chain. The JSON-Logic can use variables state, dispatch and event (the callback event).

configuration

properties to passthrough the underlying component.

parentState

if no initialState is passed it will be used to overwrite it, enables to forward through children the state when children are compatible.

parentDispatch

parent dispatch callback (to be able to modify parent state).

Registry context usage

In most applications, you will mix Dynamic with custom components. For that case it is worth injecting the registry as a context at a high level of the application:

import { render } from 'preact';
import { Dynamic, ComponentRegistryContext } from '@yupiik/preact';
import MyComponent from './my-component';

const registry = {
    component1: Component1,
    component2: Component2,
    component3: Component3,
}

render(
    <ComponentRegistryContext.Provider value={registry}>
        <MyComponent/>
    </ComponentRegistryContext.Provider>,
    document.getElementById('app'));

This simple snippet enables any nested component like MyComponent to reuse Dynamic without specifying the registry which allows to write reusable component more easily:

export const MyComponent = () => (
    <Dynamic
        options={{
            name: 'component1',
            options: {
                configuration: {
                    // whatever options component1 supports
                },
                wrapperProps: {
                    // whatever must be bound on the HoC which will wrap component1
                    // (can be useful with preact-router like components for ex which require the next immediate child to have specific props)
                },
            },
        }}
    />
);

Note about the configuration

In a real application, you will often have a tree of Dynamic.

If your backend supports bulking (JSON-RPC is highly recommended but GraphQL or other protocols can support it), we recommend you to wrap Dynamic to enable to bulk the requests by stashing them and to issue a single HTTP request for the component tree.

It is mainly a matter of abstracting the communication with the backend for the components and having a toggle to know if all subcomponents got an initial render or not.

ANT Design integration (@yupiik/antd-registry)

@yupiik/antd-registry provides a component registry which works with Dynamic or FromConfiguratonHoc.

React component based libraries (Bootstrap, Feather, …​)

react-bootstrap can be integrated importing it (import * as reactBootstrap) and converting the import to a registry using @yupiik/dynamic nestedRegistry fonction.

Similarly react-feather which does not use nested components can be converted to a registry the same way but using simpleRegistry function is more efficient instead of nestedRegistry.

Using this method on react-bootstrap, you will get in in the registry, as of today, the following list of component:

  • Accordion

  • Accordion.Button

  • Accordion.Collapse

  • Accordion.Item

  • Accordion.Header

  • Accordion.Body

  • AccordionButton

  • AccordionCollapse

  • AccordionContext

  • AccordionContext.Consumer

  • AccordionContext.Provider

  • AccordionHeader

  • AccordionItem

  • Alert

  • Alert.Link

  • Alert.Heading

  • AlertHeading

  • AlertLink

  • Anchor

  • Badge

  • Breadcrumb

  • Breadcrumb.Item

  • BreadcrumbItem

  • Button

  • ButtonGroup

  • ButtonToolbar

  • Card

  • Card.Img

  • Card.Title

  • Card.Subtitle

  • Card.Body

  • Card.Link

  • Card.Text

  • Card.Header

  • Card.Footer

  • Card.ImgOverlay

  • CardBody

  • CardFooter

  • CardGroup

  • CardHeader

  • CardImg

  • CardImgOverlay

  • CardLink

  • CardSubtitle

  • CardText

  • CardTitle

  • Carousel

  • Carousel.Caption

  • Carousel.Item

  • CarouselCaption

  • CarouselItem

  • CloseButton

  • Col

  • Collapse

  • Container

  • Dropdown

  • Dropdown.Toggle

  • Dropdown.Menu

  • Dropdown.Item

  • Dropdown.ItemText

  • Dropdown.Divider

  • Dropdown.Header

  • DropdownButton

  • DropdownDivider

  • DropdownHeader

  • DropdownItem

  • DropdownItemText

  • DropdownMenu

  • DropdownToggle

  • Fade

  • Figure

  • Figure.Image

  • Figure.Caption

  • FigureCaption

  • FigureImage

  • FloatingLabel

  • Form

  • Form.Group

  • Form.Control

  • Form.Floating

  • Form.Check

  • Form.Switch

  • Form.Label

  • Form.Text

  • Form.Range

  • Form.Select

  • Form.FloatingLabel

  • FormCheck

  • FormCheck.Input

  • FormCheck.Label

  • FormControl

  • FormControl.Feedback

  • FormFloating

  • FormGroup

  • FormLabel

  • FormSelect

  • FormText

  • Image

  • InputGroup

  • InputGroup.Text

  • InputGroup.Radio

  • InputGroup.Checkbox

  • ListGroup

  • ListGroup.Item

  • ListGroupItem

  • Modal

  • Modal.Body

  • Modal.Header

  • Modal.Title

  • Modal.Footer

  • Modal.Dialog

  • ModalBody

  • ModalDialog

  • ModalFooter

  • ModalHeader

  • ModalTitle

  • Nav

  • Nav.Item

  • Nav.Link

  • NavDropdown

  • NavDropdown.Item

  • NavDropdown.ItemText

  • NavDropdown.Divider

  • NavDropdown.Header

  • NavItem

  • NavLink

  • Navbar

  • Navbar.Brand

  • Navbar.Collapse

  • Navbar.Offcanvas

  • Navbar.Text

  • Navbar.Toggle

  • NavbarBrand

  • NavbarCollapse

  • NavbarOffcanvas

  • NavbarText

  • NavbarToggle

  • Offcanvas

  • Offcanvas.Body

  • Offcanvas.Header

  • Offcanvas.Title

  • OffcanvasBody

  • OffcanvasHeader

  • OffcanvasTitle

  • OffcanvasToggling

  • Overlay

  • OverlayTrigger

  • PageItem

  • Pagination

  • Pagination.First

  • Pagination.Prev

  • Pagination.Ellipsis

  • Pagination.Item

  • Pagination.Next

  • Pagination.Last

  • Placeholder

  • Placeholder.Button

  • PlaceholderButton

  • Popover

  • Popover.Header

  • Popover.Body

  • PopoverBody

  • PopoverHeader

  • ProgressBar

  • Ratio

  • Row

  • SSRProvider

  • Spinner

  • SplitButton

  • Stack

  • Tab

  • Tab.Container

  • Tab.Content

  • Tab.Pane

  • TabContainer

  • TabContent

  • TabPane

  • Table

  • Tabs

  • ThemeProvider

  • Toast

  • Toast.Body

  • Toast.Header

  • ToastBody

  • ToastContainer

  • ToastHeader

  • ToggleButton

  • ToggleButtonGroup

  • ToggleButtonGroup.Button

  • Tooltip

useJsonRpc hook (@yupiik/use-json-rpc)

Usage

import { render } from 'preact';
import { useJsonRpc } from '@yupiik/use-json-rpc';

export const MyComponent = ({}) => {
    const [ loading, error, data ] = useJsonRpc({
        payload: {
            jsonrpc: '2.0',
            method: 'my-server-method',
            params: {},
        },
        // optional
        endpoint: '/jsonrpc',
        needsSecurity: true,
        fetchOptions: {},
        dependencies: [],
    });

    if (loading) {
        return (<Skeleton />);
    }

    if (error) {
        return (<ErrorAlert error={error} />);
    }

    if (!data) {
        return (<div>No data.</div>);
    }

    return (<pre>{JSON.stringify(data, null, 2)}</pre>);
};

Configuration

useJsonRpc hook takes the following properties in its object parameter:

Name Default Description

payload

-

JSON-RPC request, can be an array or an object.

endpoint

/jsonrpc

JSON-RPC endpoint to call

needsSecurity

true

Should Authorization header be appended from SecurityContext.access_token value as a bearer token.

fetchOptions

{}

Any fetch option merged with computed ones from other parameters.

dependencies

[endpoint,payload,providedData]

useEffect dependencies, by default it uses the request but customizing it can enable to avoid rendering loops.

fetch

fetch

The fetch function to use, default to global javascript one.

providedData

-

The JSON-RPC data result (if provided, ie thruthy, it will be used and the server call will be bypassed).

Use SecurityContext provider

When you keep needsSecurity to true, you must pass a SecurityContext.Provider:

import { SecurityContext } from '@yupiik/use-json-rpc';

export const MyComponent = () => (
    <SecurityContext.Provider value={{access_token: ....}}>
        <MyComponentUsingUseJsonRpc />
    </SecurityContext.Provider>
);
Tip
it is often done at a high level of the application to be shared accross all components.

Build

Project uses lerna. To build all modules run:

npm i
npm run build

# optionally to run tests
npm run test