Skip to content

Latest commit

 

History

History
342 lines (246 loc) · 12 KB

README.adoc

File metadata and controls

342 lines (246 loc) · 12 KB

Kiali UI

Introduction

A UI for the Kiali Istio Observability Project

Quick Start

# 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.

Directory Structure

  • 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

Developing

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.

Running Kiali Frontend

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!

Internationalization (i18n)

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:

  1. Add a new language entry in the common type enum Language.

export const enum Language {
  CHINESE = 'zh',
  ENGLISH = 'en'
  // Add new language entry here
}
  1. 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.

  1. Add the new language in the i18next-parser.config.ts file so that the i18n-parser generates the locale file for the new language.

locales: ['en', 'zh', /* add new language code here */],
  1. Execute yarn build to generate the new locale file.

  2. 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 or useTranslation).

export const Menu = withTranslation(I18N_NAMESPACE)(MenuComponent);

const { t } = useTranslation(I18N_NAMESPACE);
  • Always import the i18n variable from i18n, not from i18next or react-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

Integration testing is done with cypress. More information can be found here.

Styling

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:

  1. Typestyle: Define dynamic styling with Typescript in a declarative, conflict-free and reusable way

  2. 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.

Style Code Guide

Testing

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

Testing Libraries for Vulnerabilities

We use github Dependabot for vulnerability testing. Dependabot runs regularly on the kiali/kiali repository.

Production Builds

Use yarn build which will bundle the build artifacts using webpack into the build directory.

See the LICENSE file.