Extension mechanism
Last updated
Last updated
ScandiPWA is not meant to be modified, rather extended. This means no changes must be done it the source theme (vendor/scandipwa/source
), rather changes in app/design/frontend/<VENDOR>/<THEME>
must be made.
You create a file with the same name, under the same folder - reference the vendor/scandipwa/source
to find the exact name and file. But in general, the algorithm is as follows:
Find out the component name using web-inspector. The name of the component can be found using following algorithm:
Using inspector find an element you want to change, i.e. .Header-Button_type_menu
element.
Because of BEM element with class .Header-Button_type_menu
is clearly related to Header
component, and must be declared there.
Knowing the component name, it is time to decide what would you like to change:
If the logic change is intended, our you plan to connect to the global state - override the <COMPONENT NAME>.container.js
.
If the presentation change is intended - you need to override the <COMPONENT NAME>.component.js
.
If styles are intended to change completely, you need to override the <COMPONENT NAME>.style.scss
.
If you only want to adjust style, you will need to create and import new additional file - <COMPONENT NAME>.style.override.scss
Note:
you are required to import this new style file in <COMPONENT NAME>.component.js
or, even in <COMPONENT NAME>.container.js
, it just must be imported.
3. Using the VSCode extension or manually create a files with the correct name in the correct folder. The folder naming logic is as follows:
If it is the component, not the route (i.e. ProductPage
, CategoryPage
, MyAccount
, Checkout
) you will find it in the app/component/<COMPONENT NAME>/
folder. For example, the Header
is located in app/component/Header/
.
If it is a page, or a route - search for it in the app/route/<COMPONENT NAME>/
.
Note:
there will never be a component folder nested in another component folder! The file-structure is flat, and consistent.
4. The file is created, what next? Time to write some JavaScript. The main idea for extension - you are replacing the file, so all exposed “API”s especially export
must be preserved. The general template looks as follows:
The following notes apply:
The <PATH TO SOURCE COMPONENT>
MUST refer to file, not the folder, i.e. instead of: component/Header
write component/Header/Header.component
.
The default export must be preserved - the new, extended class must contain the same logic, i.e. if I have following in the code: export default connect(mapStateToProps, mapDispatchToProps)(Header);
I must import the connect from redux (just like in source component), mapStateToProps
and mapDispatchToProps
from source file I am extending, and ensure my component, contains the same default export.
Importing import { A } from '...';
is not similar to: import A from '...';
first code imports the named export, second - default export. You most probably want to use the named export (the first option, with curly brackets).
Let’s now consider a common cases, to prove the algorithm works.
The original router file is located in app/route/index.js
. It is common to extend it in order to add new routes, here is a template to use:
Imagine you want to extend the Header functionality, by adding additional state to it. This requires to extend the original component and container. Here is a template for them (files are: app/component/Header.component.js
and app/component/Header.container.js
).
Cool, the state declared, now it is time to add default URL handler for it:
For styles nothing changes. You create a file under the same name and it gets included into the bundle. Sometimes, the restart of frontend container is needed, because of the webpack cache. The sole exception to this general rule is the src/app/style/abstract/_abstract.scss
file. Because it is auto-imported by webpack in all */**.scss
files. If you plan to override it:
Create the file importing original styles, like this:
2. In both webpack configurations (webpack.development.config.js
, webpack.production.config.js
) change following line:
ScandiPWA has a long-standing bug - the templates are impossibble to override. For some reason, they are always taken from vendor folder, instead of the theme. Because of that, in order to change anything inside of the src/public/index.development.html
or src/public/index.production.phtml
you must:
Rename the files, in example, from src/public/index.development.html
to src/public/my-index.development.html
, and from src/public/index.production.phtml
to src/public/my-index.production.phtml
.
Note:
we renamed from index
to my-index
! You can rename to anything else.
2. Change their webpack import declarations. In both webpack configurations (webpack.development.config.js
, webpack.production.config.js
) change following line:
3. Reload the webpack. If using docker the following command should be executed:
If using without docker - stop the server, then start it again.
The reducers are not classes. The actions are simple functions. Here is an instruction to do both: create new action, update initial state.
To extend the action, in example the src/app/store/Navigation/Navigation.action.js
. Create the file with same name and path in your theme (app/design/<VENDOR>/<THEME>
), then do following:
Extending reducer is a little more complex. The main idea is to create a switch before the original reducer, where if new / necessary action type is found - return the state update, else call original reducer. Take a look:
Creating a new file
Imagine you have to override the following file:
In order to acomplish that all you need to do is to create a file with the following path:
The pattern is: for file with the following path
You need to create a file with the following path, that is going to be taken in place of original file. Notice that to reduce the path you don’t need to reference src/scandipwa folders each and every time. They’d always be there, that’s why we removed them from the modified path to simplify file structure.
2. Retrieving the original functionality
You can import anything exported from the original plugin file to extend it, just as when extending source theme files. To do that you can reference the original file by relative path, but that is a bit too long. To make it simpler, a different approach has been implemented.
Now you can import original functionality using a generated alias, that depends on the package name in composer.json
file in the root of your extension. It consists of vendor name and extension name, written in PascalCase and separated by underscore.
So if you need to import something from the scandipwa/paypal-graphql
package you can do it by referencing the original file by following alias, anywhere throughout the application:Copy
Note:
remember that these aliases are case-sensitive. PayPalGraphQL instead of PaypalGraphql will throw errors
3. Extending
See more on extending functionality with such approach (using ScandiPWA Fallback plugin) above. All extensions-related specifics have been described in this article.
Other component extension is similar. Please re-read the step-by-step algorithm, this really helps!
Are you still struggling? Join the Slack channel and do not hesitate to share your problems there!