README
mono-components
About this package
This package is only used in the mononoth, through an automated hypernova integration.
See the related hypernova-app repo.
Description
General Aim
The Aim of this package is to have a point of contact between the shared-components
package and the Mononoth to facilitate the disintegration of existing Mononoth-pages and allow for progressive iteration of new functionality.
Most of the development happens in shared-components, where we iterate on React components that can be shared between all frontend-services and the mononoth. However, there will be a number of functions that should not be part of shared-components because they will only ever be used in the Mononoth. This package takes responsibility for owning that functionality.
End Goal
mono-components
should never be seen as a place where functionality will reside forever.
The aim of adding functionality inside of this package is to evolve components from shared-components so that they can be used in a disintegrated page as well other frontend-services.
Once the affected page is disintegrated into a frontend-service, any associated functionality in mono-components ceases to exist. At which point the relevant page is also removed from the mononoth.
Noths Stream Ownership
Owned by all Streams
Owned Routes or Domains
Routes aren't "owned" but this package affects the following routes in the Mononoth.
Consumer:
- /search (and all related search pages: category, partner, departments)
- /checkout
API Dependencies
none
Used NOTHS NPM Packages
- @noths/global-components
- @noths/shared-components
- @noths/code-style
- @noths/eslint-config
Installation & Development
Installation
# Install dependencies
npm i
Developing in mono-components only
# Run in dev mode (watches files & hot-reloads client-code)
npm run dev
Hybrid Development with shared-components & mono-components
Preparation:
# In frontend-packages root folder, link all packages together
# If you run into issues, you can try `npm run clean` before bootstrapping
npm run bootstrap
# A second method is linking only shared-components with mono-components
cd packages/shared-components
npm link
cd ../mono-components
npm link @noths/shared-components
Next up, you'll want to watch shared-component changes while developing in mono-components.
In terminal 1 (mono-components):
cd packages/mono-components
npm run dev
In terminal 2 (shared-components):
cd packages/shared-components
npm run watch
Now, if you make changes to something inside shared-components, after some time it should hot-reload in your browser. If you watch the logs in both terminals you should see terminal 2 update and then terminal 1 and finally the browser.
Hybrid Development with Mononoth & mono-components & shared-components
Preparation:
NOTE:
Don't link with lerna during this process!
This method is in an experimental state for and all thenode_modules
removing might be overkill, if you can improve this method, be my guest! Make sure your suggested method really works, then make a PR :)
# This will remove all node_modules and package-lock.json files.
# It will make sure you're on a completely clean base for development
cd packages/shared-components
npm run experimental:hardlink:rome
cd ../mono-components
npm run experimental:hardlink:rome
# Now in the `hypernova-app`-repo root, run the same command as well
npm run experimental:hardlink:rome
Any generated package-lock
-files should not be commited.
Next up, you'll want to watch shared-component changes while developing in the Mononoth.
This is quite slow, so only do this when necessary. It is recommended to do most of your development in mono-components using the first Hybrid approach. That approach offers hot-reload and much faster development.
In terminal 1 (notonthehighstreet):
- change the file
notonthehighstreet/config/application/defaults/other.yml
(uncomment/recomment the lines concerning hypernova) - Run the required command(s) to start the mononoth.
In terminal 2 (hypernova-app):
# This will have to be restarted if you want to get rid of the content mistmatch error
# after making changes to either mono-components or shared-components
npm run start:dev
In terminal 3 (shared-components):
# This will watch the files on any changes
npm run watch
In terminal 4 (mono-components):
# This will build the files only once (watch-mode doesn't exist in mono-components)
npm run build
For information about dev-toolkit, refer to the documentation.
Development Workflow in mono-components
- Add a new page
# make folder where components will reside
mkdir ./src/client/pages/my_page
# create a template where components will be rendered
touch ./src/server/views/my_page.hbs
And add the following content to my_page.hbs
<!-- NOTE: This template is for development only and will not be used in production. -->
<!-- Rendered Component HTML -->
{{{ renderComponent 'MyPage_ExampleComponent' props='{ ... }' }}}
<!-- CSS for faking component placement in the Mononoth -->
<style>
.MONO-MyPage_ExampleComponent{
position: relative;
display: block;
float: left;
border: 2px dashed #42A5F5;
}
</style>
- Add a new component
mkdir ./src/client/pages/my_page/ExampleComponent
touch ./src/client/pages/my_page/ExampleComponent/index.js
- Add a new component to
./src/client/index.js
export { default as MyPage_ExampleComponent } from './pages/my_page/ExampleComponent';
- Add new page route to
./src/settings.js
{ route: '/my_page', components: ['ExampleComponent'] },
You're now ready to start developing your ExampleComponent
.
npm run dev
How to use in the Mononoth
You will only be able to use components in the Mononoth that have been defined in src/client/index.js
.
In order to use these components, you will need to:
- publish the
@noths/mono-components
package - bump the version of the package in hypernova-app
- create a PR with the version bump
- verify that everything works as expected in QA
- release to production by merging the PR
In order to test your components in development with the Mononoth after bumping the version in hypernova-app
, you will need to uncomment/comment-out the lines in the mononoth that specifies where the integration file will be served from before starting the Mononoth and the hypernova-app
server.
Main steps of how to render a component in the Mononoth:
In your .erb
view file:
<%= render_react_component(
'Search_Sidebar',
exampleProp: 'exampleText',
filters: { ... }
) if hypernova_enabled? %>
At the end of your view layout file:
<%= include_hypernova_integration_script %>
In the controller that renders your view using the above layout:
class ExampleController
include HypernovaRenderHelper
around_action :hypernova_render_support, if: :hypernova_enabled?
...
end
A note on caching
You must ensure that your rendered mono-component is not inside a fragment-cached block. These blocks usually start with the <% cache
-prefix.
You might also want to look into other forms of Rails caching such as "Action Caching".
In essence, you must "carve out" a path to your component that will not be cached through Rails in any way. The handling of component cache is done in hypernova itself.
A note on component-props
In order to render your component correctly, you will need to collect some data from the Mononoth to pass into your component as props. How to get this data will vary, it is suggested you find someone who is comfortable in working with Rails to help with this.
How to test
It is a bit difficult to test hypernova renders since they are async. We can however do some basic checks.
require 'feature_spec_helper'
# The view that renders your component
describe 'shared/_search_sidebar', type: :view do
# Specify what the name of the component is
let(:component_name) { "Search_Sidebar" }
# Specify the props that you expect your component to receive
let(:props) do
{
exampleProp: 'exampleText',
filters: { ... }
}
end
# Specify a placeholder that will be returned to make sure that something will get rendered
let(:component_placeholder) { "dummy text" }
before(:each) do
# We need this to render the view
allow(view).to receive(:current_filters).and_return({})
allow(view).to receive(:filter_groups).and_return({ price: [] })
# Mock test helpers
allow(view).to receive(:render_react_component).with(component_name, props).and_return(component_placeholder)
end
# Example tests with feature flag on and off
context "with feature flag on" do
it do
# turn the flag on
allow(view).to receive(:feature_on?).with(:hypernova).and_return(true)
render
expect(view).to have_received(:render_react_component).with(component_name, props)
expect(rendered).to include(component_placeholder)
end
end
context "with feature flag off" do
it do
render
expect(view).not_to have_received(:render_react_component).with(component_name, props)
expect(rendered).not_to include(component_placeholder)
end
end
end
Building for Production
# Generate build for publishing npm package
npm run build
Publishing
Release version
Please refer to the frontend-packages Readme for the release process of patch, minor or major versions.
Releasing a Beta version
While logged into npm with your username, run:
npm run prerelease
Testing & Code-Style
# Run unit tests
npm test
# Format all files using prettier settings
npm run format
# Lint all javascript files
npm run lint
Feature-Test Workflow
none as of now
Error Logging
No logging is being used in this package.
CI
CI is done via lerna in frontend-packages root
Analytics
No Analytics is being done in this package.