-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🌱 Setup branding to be configurable during the build (#1664)
## Summary Refactor and enhance branding capabilities. Now branding is included from a single directory that contains at least `strings.json`, `manifest.json`, and `favicon.ico`. Any other assets may be placed in the directory and will be copied to the bundled app. Running the build as normal should show no difference. Running the build with `BRANDING=./some-other-brand npm run start:dev` will try to use a branding from the specified directory (that is relative to the project's source root). See `BRANDING.md` for more details. Resolves: #1682 ## Details Branding changes: - Move Konveyor branding assets to a project top-level branding directory - Remove MTA branding assets - Remove profile/branding constants from `env` and client module - Embed branding strings and assets in the common module - `strings.json` is templated to allow the build to adjust asset URL path roots as necessary - `brandingAssetPath()` - server's index.html generation sources the template strings from the common module's branding strings - `HeaderApp` and `AppAboutModal` components support branding by using the `useBranding` hook - `BRANDING` as a relative path is computed from the project root - webpack build source branding assets directly from the common module - Unit tests, snapshots and jest configs updated as necessary Jest changes: - Use `react-i18next` mock from `client/__mocks__` as a more robust mock borrowed from react-i18n repos - Move `setupTests.ts` into `client/src/app/test-config` to keep jest test config code all in the same directory Related changes: - Upgrade rollup to v4, add new rollup plugins (copy, virtual) --------- Signed-off-by: Scott J Dickerson <[email protected]> Co-authored-by: Ian Bolton <[email protected]> Signed-off-by: Cherry Picker <[email protected]>
- Loading branch information
Showing
44 changed files
with
899 additions
and
307 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
# Branding | ||
|
||
The UI supports static branding at build time. Dynamically switching brands is not | ||
possible with the current implementation. | ||
|
||
## Summary | ||
|
||
Each of the project modules need to do some branding enablement. | ||
|
||
- `@konveyor-ui/common` pulls in the branding assets and packages the configuration, | ||
strings and assets within the common package. The other modules pull branding | ||
from the common module. | ||
|
||
- `@konveyor-ui/client` uses branding from the common package: | ||
|
||
- The location of `favicon.ico`, `manifest.json` and any other branding | ||
assets that may be referenced in the `brandingStrings` are sourced from the | ||
common package. | ||
|
||
- The `brandingStrings` are used by the dev-server runtime, to fill out the | ||
`index.html` template. | ||
|
||
- The about modal and application masthead components use the branding strings | ||
provided by the common module to display brand appropriate logos, titles and | ||
about information. Since the common module provides all the information, it | ||
is packaged directly into the app at build time. | ||
|
||
- `@konveyor-ui/server` uses the `brandingStrings` from the common package to fill | ||
out the `index.html` template. | ||
|
||
## Providing alternate branding | ||
|
||
To provide an alternate branding to the build, specify the path to the branding assets | ||
with the `BRANDING` environment variable. Relative paths in `BRANDING` are computed | ||
from the project source root. | ||
|
||
Each brand requires the presence of at least the following files: | ||
|
||
- `strings.json` | ||
- `favicon.ico` | ||
- `manifest.json` | ||
|
||
With a file path of `/alt/custom-branding`, a build that uses an alternate branding | ||
is run as: | ||
|
||
```sh | ||
> BRANDING=/alt/custom-branding npm run build | ||
``` | ||
|
||
The dev server can also be run this way. Since file watching of the branding assets | ||
is not implemented in the common module's build watch mode, it may be necessary to | ||
manually build the common module before running the dev server. When working on a | ||
brand, it is useful to run the dev server like this: | ||
|
||
```sh | ||
> export BRANDING=/alt/custom-branding | ||
> npm run build -w common | ||
> npm run start:dev | ||
> unset BRANDING # when you don't want to use the custom branding path anymore | ||
``` | ||
|
||
### File details | ||
|
||
#### strings.json | ||
|
||
The expected shape of `strings.json` is defined in [branding.ts](./common/src/branding.ts). | ||
|
||
The default version of the file is [branding/strings.json](./branding/strings.json). | ||
|
||
A minimal viable example of the file is: | ||
|
||
```json | ||
{ | ||
"application": { | ||
"title": "Konveyor" | ||
}, | ||
"about": { | ||
"displayName": "Konveyor" | ||
}, | ||
"masthead": {} | ||
} | ||
``` | ||
|
||
At build time, the json file is processed as an [ejs](https://ejs.co/) template. The | ||
variable `brandingRoot` is provided as the relative root of the branding | ||
assets within the build of the common module. Consider the location of `strings.json` | ||
in your branding directory as the base `brandingRoot` when creating a new brand. | ||
|
||
For example, to properly reference a logo within this branding structure: | ||
|
||
``` | ||
special-brand/ | ||
images/ | ||
masthead-logo.svg | ||
about-logo.svg | ||
strings.json | ||
``` | ||
|
||
Use a url string like this: | ||
|
||
```json | ||
{ | ||
"about": { | ||
"imageSrc": "<%= brandingRoot %>/images/about-logo.svg" | ||
} | ||
} | ||
``` | ||
|
||
and in the output of `BRANDING=special-brand npm run build -w common`, the `imageSrc` | ||
will be `branding/images/about-logo.svg` with all of the files in `special-branding/*` | ||
copied to and available to the client and server modules from | ||
`@konveyor-ui/common/branding/*`. | ||
|
||
#### favicon.ico | ||
|
||
A standard favorite icon file `favicon.ico` is required to be in the same directory | ||
as `strings.json` | ||
|
||
#### manifest.json | ||
|
||
A standard [web app manifest](https://developer.mozilla.org/en-US/docs/Web/Manifest) | ||
file `manifest.json` is required to be in the same directory as `strings.json`. | ||
|
||
## Technical details | ||
|
||
All branding strings and assets are pulled in to the common module. The client and | ||
server modules access the branding from the common module build. | ||
|
||
The `common` module relies on rollup packaging to embed all of the brand for easy | ||
use. The use of branding strings in `client` and `server` modules is straight forward. | ||
Pulling in `strings.json` and providing the base path to the brand assets is a | ||
more complicated. | ||
|
||
The `common` module provides the `brandingAssetPath()` function to let the build time | ||
code find the root path to all brand assets. Webpack configuration files use this | ||
function to source the favicon.ico, manifest.json and other brand assets to be copied | ||
to the application bundle. | ||
|
||
The `brandingStrings` is typed and sourced from a json file. To pass typescript builds, | ||
a stub json file needs to be available at transpile time. By using a typescript paths | ||
of `@branding/strings.json`, the stub json is found at transpile time. The generated | ||
javascript will still import the path alias. The | ||
[virtual rollup plugin](https://github.com/rollup/plugins/tree/master/packages/virtual) | ||
further transform the javascript output by replacing the `@branding/strings.json` import | ||
with a dynamically built module containing the contents of the brand's `strings.json`. | ||
The brand json becomes a virtual module embedded in the common module. | ||
|
||
A build for a custom brand will fail (1) if the expected files cannot be read, or (2) | ||
if `strings.json` is not a valid JSON file. **Note:** The context of `stings.json` is | ||
not currently validated. If something is missing or a url is malformed, it will only | ||
be visible as a runtime error. |
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"application": { | ||
"title": "Konveyor", | ||
"name": "Konveyor Tackle UI", | ||
"description": "Konveyor/Tackle UI" | ||
}, | ||
"about": { | ||
"displayName": "Konveyor", | ||
"imageSrc": "<%= brandingRoot %>/images/masthead-logo.svg", | ||
"documentationUrl": "https://konveyor.github.io/konveyor/" | ||
}, | ||
"masthead": { | ||
"leftBrand": { | ||
"src": "<%= brandingRoot %>/images/masthead-logo.svg", | ||
"alt": "brand", | ||
"height": "60px" | ||
}, | ||
"leftTitle": null, | ||
"rightBrand": null | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/* eslint-env node */ | ||
|
||
// Adapted from https://github.com/i18next/react-i18next/blob/master/example/test-jest/src/__mocks__/react-i18next.js | ||
import React from "react"; | ||
import * as reactI18next from "react-i18next"; | ||
|
||
const hasChildren = (node) => | ||
node && (node.children || (node.props && node.props.children)); | ||
|
||
const getChildren = (node) => | ||
node && node.children ? node.children : node.props && node.props.children; | ||
|
||
const renderNodes = (reactNodes) => { | ||
if (typeof reactNodes === "string") { | ||
return reactNodes; | ||
} | ||
|
||
return Object.keys(reactNodes).map((key, i) => { | ||
const child = reactNodes[key]; | ||
const isElement = React.isValidElement(child); | ||
|
||
if (typeof child === "string") { | ||
return child; | ||
} | ||
if (hasChildren(child)) { | ||
const inner = renderNodes(getChildren(child)); | ||
return React.cloneElement(child, { ...child.props, key: i }, inner); | ||
} | ||
if (typeof child === "object" && !isElement) { | ||
return Object.keys(child).reduce( | ||
(str, childKey) => `${str}${child[childKey]}`, | ||
"" | ||
); | ||
} | ||
|
||
return child; | ||
}); | ||
}; | ||
|
||
const useMock = [(k) => k, { changeLanguage: () => new Promise(() => {}) }]; | ||
useMock.t = (k) => k; | ||
useMock.i18n = { changeLanguage: () => new Promise(() => {}) }; | ||
|
||
module.exports = { | ||
Trans: ({ children, i18nKey }) => | ||
!children | ||
? i18nKey | ||
: Array.isArray(children) | ||
? renderNodes(children) | ||
: renderNodes([children]), | ||
|
||
Translation: ({ children }) => children((k) => k, { i18n: {} }), | ||
|
||
useTranslation: () => useMock, | ||
|
||
initReactI18next: { | ||
type: "3rdParty", | ||
init: () => {}, | ||
}, | ||
|
||
// mock if needed | ||
withTranslation: reactI18next.withTranslation, | ||
I18nextProvider: reactI18next.I18nextProvider, | ||
setDefaults: reactI18next.setDefaults, | ||
getDefaults: reactI18next.getDefaults, | ||
setI18n: reactI18next.setI18n, | ||
getI18n: reactI18next.getI18n, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.