# Get the kiali-ui sources
git clone https://github.com/kiali/kiali.git
cd kiali/frontend
# Install Yarn
npm install -g yarn
# Install kiali-ui dependencies
yarn install
# Start a development server
yarn start
Note
|
On some platforms, yarn start may fail with an error like Error: ENOSPC: System limit for number of file watchers reached . To fix this, you need to increase the limit of file watchers on your system. The command may vary depending on the platform, please refer to these instructions.
|
A new browser window should automatically open.
But, if it doesn’t then use: http://localhost:3000
(or whatever address is told by the yarn start
command).
This launches a development environment that instantly reloads any changes to the browser for rapid development.
-
build
: Production build output -
public
: Home of index.html -
src
: Source and test code -
src/actions
: Redux actions -
src/app
: React top level component -
src/assets
: Images and other assets -
src/components
: React Components -
src/config
: Configuration -
src/helpers
: Utility functions and components -
src/hooks
: Utility hooks -
src/pages
: Top level pages and nested components -
src/reducers
: Redux reducers -
src/routes
: Router wrapper components -
src/services
: Api services -
src/store
: Redux store definitions -
src/styles
: Application wide styles -
src/types
: Typescript definitions for all kinds of types -
src/utils
: Various Utilities
The Kiali UI is a React application written in Typescript.
We use yarn
as the package manager, if adding dependencies to package.json
make sure you install them with yarn
and commit the yarn.lock
file.
When developing, it is typical to run the frontend outside of the kiali server in the cluster.
It is possible to proxy the API requests without having to push the changes to kiali.
Add the proxy
property to package.json
with the url of kiali.
{
"proxy": "http://localhost:20001",
"name": "@kiali/kiali-ui",
"version": "1.82.0",
}
If Kiali is configured with a specific web root, make sure to append it to the URL. On many setups with Kubernetes, the web root will be /kiali
by default.
Run yarn start
and try it!
Kiali supports internationalization with the react-i18next
library. String literals to be translated are added automatically to a locale file (stored in public/locales/<language>/translation.json
), during the development phase, with i18n-parser
(executed along with yarn start
or yarn build
command). At runtime, the i18n library will use these locale files to perform translation to a specific language.
Adding a new language
These are the steps to add a new language to Kiali:
-
Add a new language entry in the
common type
enumLanguage
.
export const enum Language {
CHINESE = 'zh',
ENGLISH = 'en'
// Add new language entry here
}
-
Add a new select entry for the new language in the component
LanguageSwitch.tsx
<DropdownItem key="Chinese" onClick={() => switchLanguage(Language.CHINESE)}>
<span>中文</span>
{language === Language.CHINESE && <KialiIcon.Check className={checkStyle} />}
</DropdownItem>
If possible, include a new language test case scenario in the language selector unit test LanguageSwitch.test.tsx
.
-
Add the new language in the
i18next-parser.config.ts
file so that thei18n-parser
generates the locale file for the new language.
locales: ['en', 'zh', /* add new language code here */],
-
Execute
yarn build
to generate the new locale file. -
Modify the generated locale file as outlined in the next section.
Modify an existing language translation
The locale files are stored in public/locales/<language>/translation.json
. If you find a wrong translation or an English statement (default value when the label is generated), simply modify that file to include the correct language translation.
Add i18n support to a React component
The way to adapt a React component to the i18n framework depends on its type:
-
React class:
Use the withTranslation
high order component. More info here
You must embed the React class with the withTranslation
function (similar to redux connect function):
export const Menu = withTranslation(I18N_NAMESPACE)(MenuComponent);
Also, WithTranslation
props must be added to the React class properties in order to include the t
translation function (like Redux properties)
type MenuProps = WithTranslation & {
...
};
Then you will be able to use the t
function, like any other React class property, to translate any label (variable or string literal).
<h1>{this.props.t(title)}</h1>
-
React hook:
Use the useTranslation
hook to get the t
translation function. More info here.
Any label (variable or string literal) can be translated with the`t` function within the component.
const { t } = useTranslation(I18N_NAMESPACE);
<h1>{t(title)}</h1>
-
External variable:
String literals that are stored in a variable defined outside of a React component cannot be translated directly. Instead, the translation has to be done when the variable is used within the React component (class or hook).
The problem is that the i18n parser can’t extract labels from variables, only from literals. To indicate to the i18n parser to add that kind of string literal to the locale file, use the`ì18n.t` function. Note that i18n.t
does not perform any translation, it returns the same string literal at runtime (only for development purposes).
import { i18n } from 'i18n';
const overviewTypes = {
app: i18n.t('Apps'),
workload: i18n.t('Workloads'),
service: i18n.t('Services')
};
const Example: React.FC<Props> = (props: Props) => {
const { t } = useTranslation(I18N_NAMESPACE);
return (
<span>{t(overviewTypes.app)}</span>
...
)
}
-
External function:
External functions declared outside of React components cannot use the t
function directly. Instead, it must be passed as an input parameter (TFunction
type must be imported from the react-i18next
library, it will fail if imported from i18next
library):
import { TFunction, useTranslation } from 'react-i18next';
const externalFunction = (t: TFunction): React.ReactNode => {
return (
...
<div>{t('Issuer:')}</div>
...
)
}
const Example: React.FC<Props> = (props: Props) => {
const { t } = useTranslation(I18N_NAMESPACE);
return (
<Tooltip position={TooltipPosition.top} content={externalFunction(t)}>
...
</Tooltip>
)
}
Special i18n cases:
-
Interpolation:
You can include any variable value within the translated statement with interpolation. More info here
<div>{t('Kiali home cluster: {{clusterName}}', { clusterName: homeCluster?.name })}</div>
The variables are represented with {{$variable}}
. You can set any name to the variable, you have just to be sure that it matches the json field.
This is how it looks like in the translation file:
"Kiali home cluster: {{clusterName}}": "Kiali主集群: {{clusterName}}",
-
Plurals:
When a statement is different in singular and plural, you can use defaultValue_one
and defaultValue_other
fields to add different plural statements (no need to add 's' manually anymore). Note that in this case the variable name must be count
. For convenience the key will be the singular statement. More info here
labelsInfo = this.props.t('{{count}} label', {
count: labelsLength,
defaultValue_one: '{{count}} label',
defaultValue_other: '{{count}} labels'
});
Additional i18n notes:
-
Always add namespace variable
I18N_NAMESPACE
to any React component translation configuration (withTranslation
oruseTranslation
).
export const Menu = withTranslation(I18N_NAMESPACE)(MenuComponent); const { t } = useTranslation(I18N_NAMESPACE);
-
Always import the i18n variable from
i18n
, not fromi18next
orreact-i18next
libraries
import { i18n } from 'i18n';
-
If the React class component contains static methods, you have to use the`hoistNonReactStatics` library to copy static methods to translated components
const OverviewToolbarI18n = hoistNonReactStatics( withTranslation(I18N_NAMESPACE)(OverviewToolbarComponent), OverviewToolbarComponent );
-
If you need the translated class component to include a React reference to access DOM nodes, use
{ withRef: true }
option:
export const StatefulFilters = withTranslation(I18N_NAMESPACE, { withRef: true })(StatefulFiltersComponent);
Integration testing is done with cypress. More information can be found here.
PatternFly is the main UI components framework. It defines style based on SASS preprocessor. All Patternfly build assets are imported from patternfly library.
Kiali uses two ways to add custom styles to any React component:
-
Typestyle: Define dynamic styling with Typescript in a declarative, conflict-free and reusable way
-
CSS Modules: CSS file in which all class names and animation names are scoped locally.
All styles defined by these two approaches are scoped to their respective component, providing encapsulation without affecting any styling defined outside of the component.
Recommended option in Kiali is Typestyle since it provides more flexibility and utilities than CSS modules. In case of big CSS styles (e.g. slider component) or definition of CSS variables (not supported by Typestyle), then CSS modules is the preferred method.
See the STYLE CODE GUIDE file.
To run the tests, the standard command yarn test
is valid,
but since some tests are using snapshots the preferred command is to update them in the same time:
yarn test -u
Then hit a
to run all tests.
After running the tests, if any snapshot has been modified (check git status), the diff should be reviewed to make sure it’s an intentional or trivial change. Modified snapshots must be part of the commit so that they’re reviewed collectively and won’t make CI build fail.
Note: for OS/X users testing requires watchman to be installed
brew install watchman
We use github Dependabot for vulnerability testing. Dependabot runs regularly on the kiali/kiali
repository.
Use yarn build
which will bundle the build artifacts using webpack into the build
directory.
See the LICENSE file.