Skip to content

Creating a Module

esr360 edited this page Jun 24, 2019 · 49 revisions

Learn more about Synergy modules

A Synergy module is essentially a UI component that has been broken up into the following areas of concern:

Each area of concern is to be handled independently without having strict dependencies on each other; i.e if you remove the styles aspect of a Synergy module, you should still be able to render a functional, but styleless UI component. Likewise, if you remove the interactions aspect of a Synergy module, you should still be able to render a styled, but functionless component etc. This is to simply allow for portability; if your project used React and decided to switch to another framework at some point, it would be very easy to port your Synergy UI modules over as you would only have to change the interface aspect.

This is an opinionated guide to building a Synergy module using React and Cell for styles

Cell was built to tackle the styles area of concern with regard to a Synergy module, using Sass as the technology. An alternative is Polymorph, which solves the same problem only using JavaScript as the technology.

This guide will walk through how to create a Synergy module using Cell for styling. For the bigger picture, see the Creating a Module page from the Synergy repository.

Example Structure

As we are using Cell, our styles for the example module will be handled with an scss file. For interactions we will use plain JavaScript. For the interface, we will use React. For configuration we will use JSON, so it can be shared between the assets (jsx, js and scss files). This might leave us with a setup like this:

modules/
|    |-- accordion/
|    |    |-- accordion.js
|    |    |-- accordion.json
|    |    |-- accordion.jsx
|    |    |-- accordion.scss

An alternative approach might be structuring the accordion files like so:

modules/
|    |-- accordion/
|    |    |-- assets/
|    |    |    |-- interactions.js
|    |    |    |-- config.json
|    |    |    |-- styles.scss
|    |    |-- accordion.jsx

Interface (React - JSX)

Learn more about Synergy module interfaces

The interface in a Synergy module is the place where interactions and styles converge to create the end result that a user interacts with. This could be achieved using various methods and technologies (e.g. Handlebars, plain HTML, PHP, Web Components etc.), but for this example we will use React and Lucid.

Lucid is a React library for rendering Synergy modules

/modules/accordion/accordion.jsx
// Import Lucid library
import { Module, Component } from '@onenexus/lucid';

// Import module assets
import config from './assets/config.json';
import interactions from './assets/interactions.js';
import styles from './assets/styles.scss';

const Accordion = ({ panels }) => (
    <Module config={config}>
        {panels.map(({title, content}) => (
            <Component name='panel'>
                <Component name='title' onClick={interactions.toggle}>
                    {title}
                </Component>

                <Component name='content'>{content}</Component>
            </Component>
        ))}
    </Module>
);

export default Accordion;

Styles (Cell - Sass)

From our interface we can identify the following components:

  • panel
  • title
  • content

...which allows for the foundation of the Sass file:

/modules/accordion/assets/styles.scss
// later we can remove passing the module name here as it will
// automatically be taken from the configuration 
@include module('accordion') {
    @include component('panel') {
        ...
    }

    @include component('title') {
        ...
    }

    @include component('content') {
        ...
    }
}

Importing Configuration

Learn more about module configuration

Learn more about importing JavaScript/JSON in Sass using Cell

/modules/accordion/assets/styles.scss
@import 'config.json';

@include module() {
    @include component('panel') {
        ...
    }

    @include component('title') {
        ...
    }

    @include component('content') {
        ...
    }
}
  • accordion argument has been removed from module(); value will now automatically be retreived from $config's name property
  • this() can now be called to retrieve a value from the configuration

Isolating Configurable Styles

So far we have used the Module and Component mixins. The accordion.scss file is where will we keep fundamental style properties for the accordion. Fundamental style properties include:

  • Properties that determine layout/structure
  • Properties that are unlikely to ever change
  • Properties that should not be configurable

One of the benefits of Synergy is the ability to isolate configurable styles from your module's source code. The idea is that if a CSS property exists only for cosmetic effect, then it does not need to be hard coded in the module. A general rule of thumb is that CSS properties which account for continuous data can be configurable (but don't have to be) and will typically be responsible for cosmetic properties; properties which account for discrete data cannot be configurable and will generally not be responsible for cosmetic properties.

Learn more about cosmetic vs layout CSS properties

Adding all the discrete data (fundamental/layout styles) to our Accordion styles.scss file might leave us with something like:

modules/accordion/assets/styles.scss
@import 'config.json';

@include module {
    @include component('panel') {
        @include modifier('active') {
            @include component('content') {
                display: block;
            }
        }
    }

    @include component('title') {
        cursor: pointer;
    }

    @include component('content') {
        display: none;
    }
}
  • We have introduced the modifier mixin to toggle visibility with the presence of an active modifier

For the continuous data, which will effectively be the accordion's cosmetic data, we will allow this to be configurable, so it will be added to the accordion's configuration.

Configuration (JSON)

Learn more about module configuration

This file is to be used to contain all configurable aspects of the module, regardless of whether or not they relate to styles, interactions or rendering. As Cell is able to automagically match configuration property keys to CSS properties, this allows you to effectively make cosmetic changes to your module without touching any source code (by just modifying the config file).

See the JavaScript Configuration page to get setup using JavaScript/JSON for configuration with Cell

modules/accordion/assets/config.json
{
    "accordion": {
        "name": "accordion",
        "panel": {
            "modifier(active)": {
                "component(title)": {
                    "background": "#2E3882",
                    "border-color": "#2E3882",
                    "color": "white"
                }
            }
        },
        "title": {
            "background": "transparent",
            "color": "#444444",
            "border": "1px solid rgba(black, 0.15)",
            "padding": "1em",
            "transition": "0.4s",
            ":hover": {
                "background": "#384BC9",
                "border-color": "#384BC9",
                "color": "white"
            }
        },
        "content": {
            "background": "white",
            "color": "#444444",
            "border": "1px solid rgba(black, 0.15)",
            "padding": "1.5em"
        }
    }
}

Want to use themes? Checkout the ]JavaScript Configuration page]((https://github.com/One-Nexus/Cell/wiki/JavaScript-Configuration#file-exports-a-function))

Interactions (JavaScript)

Learn more about module interactions

Earlier we saw in the module interface the importing of interactions.js and use of interactions.toggle on the title component - this file might look something like:

modules/accordion/assets/interactions.js
import config from './config.json';

export default {
    toggle
}

export function toggle(event) {
    const toggleClassName = `${config.name}_panel-active`;
    const toggleElement = event.target.closest(`.${config.name}_panel`);

    toggleElement.classList.toggle(toggleClassName);
}
Using Synergy framework or sQuery library

Learn More about Synergy Learn More about sQuery

export default {
    toggle
}

export function toggle(event) {
    event.target.parent('panel').toggleModifier('active');
}

Demo

It's now possible to render a complete working accordion using what we've created.

Ensure your webpack configuration is setup to pass Synergy-Sass-Importer to your sass-loader

<!-- Container in which to render the React screen -->
<div id="demo"></div>

Checkout Polymorph to style your Synergy modules using JavaScript

import React from 'react';
import ReactDOM from 'react-dom';

import Accordion from './modules/accordion/accordion.jsx';

const Screen = () => (
    <Accordion panels={[
        {title: 'foo', content: 'bar'},
        {title: 'fizz', content: 'buzz'},
    ]} />
)

ReactDOM.render(<Screen />, document.getElementById('demo'));
CSS Output

The following CSS will be output by webpack into your app:

[class*="accordion_panel-"][class*="-active"] .accordion_content, 
[class*="accordion_panel-"][class*="-active"] [class*="accordion_content-"] {
  display: block;
}

[class*="accordion_panel-"][class*="-active"] .accordion_title, 
[class*="accordion_panel-"][class*="-active"] [class*="accordion_title-"] {
  background: #2E3882;
  border-color: #2E3882;
  color: white;
}

.accordion_title, [class*="accordion_title-"] {
  display: block;
  cursor: pointer;
  background: transparent;
  color: #444444;
  border: 1px solid rgba(0, 0, 0, 0.15);
  padding: 1em;
  transition: 0.4s;
}

.accordion_title:hover, [class*="accordion_title-"]:hover {
  background: #384BC9;
  border-color: #384BC9;
  color: white;
}

.accordion_content, [class*="accordion_content-"] {
  display: none;
  background: white;
  color: #444444;
  border: 1px solid rgba(0, 0, 0, 0.15);
  padding: 1.5em;
}

Clone this wiki locally