Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions collaborating/developing.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,26 @@ To begin, ensure you have network access. Then, you'll need the following
gh repo clone IFRCGo/go-web-app #Github CLI
```

2. Install the dependencies
2. Initialize submodules

```bash
git submodule update --init --recursive --remote
```

3. Install the dependencies

```bash
pnpm install
```

3. Create a `.env` file in the `app` directory and add variables from [env.ts](https://github.com/IFRCGo/go-web-app/blob/develop/app/env.ts). Any variables marked with `.optional()` are not mandatory for setup and can be skipped.
4. Create a `.env` file in the `app` directory and add variables from [env.ts](https://github.com/IFRCGo/go-web-app/blob/develop/app/env.ts). Any variables marked with `.optional()` are not mandatory for setup and can be skipped.

```bash
cd app
touch .env
```

4. Start the development server:
5. Start the development server:

```bash
pnpm start:app
Expand All @@ -46,7 +52,7 @@ To begin, ensure you have network access. Then, you'll need the following
> \[!NOTE]\
> To work on a specific development task, ensure you have the backend setup appropriately and configured properly.

5. Create a new branch for your work.
6. Create a new branch for your work.
The stable branch for IFRC GO is `develop`, and all PRs for deployment should be merged into it.

```bash
Expand All @@ -60,7 +66,7 @@ To begin, ensure you have network access. Then, you'll need the following
* Fix Branch: For bug fixes, create branches under `fix/issue-description`.
* Chore Branch: For maintenance tasks, create branches under `chore/task-name`.

6. Once you're happy with your changes, add and commit them to your branch.If your workflow requires changesets (e.g., when changes will impact versioning or are part of a release), create one before committing. Then push the branch to origin.
7. Once you're happy with your changes, add and commit them to your branch. If your workflow requires changesets (e.g., when changes will impact versioning or are part of a release), create one before committing. Then push the branch to origin.

```bash
# Stage all changes
Expand All @@ -81,7 +87,7 @@ To begin, ensure you have network access. Then, you'll need the following
> \[!NOTE]\
> Review the [Changesets documentation](./release.md#changesets) and the [versioning guidelines](./release.md#versioning-guidelines-for-ifrc-go-project) for more details on versioning and tracking changes.

7. Create a Pull Request.
8. Create a Pull Request.
Please read the [Issues and Pull Requests](./issues-and-pull-requests.md) guide for further information.

> \[!IMPORTANT]\
Expand Down
10 changes: 8 additions & 2 deletions collaborating/repository-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The [app](../app/) directory contains the main codebase for the web application
* [app/generated/](../app/generated/) contains the API endpoint type definitions generated by [openapi-typescript](https://github.com/openapi-ts/openapi-typescript).

* [app/scripts/](../app/scripts/) contains scripts executed as part of the project.
* [app/scripts/translate/](../app/scripts/translate/) contains scripts for synchronizing translations between the application code and the server.
* [app/scripts/translatte/](../app/scripts/translatte/) contains scripts for synchronizing translations between the application code and the translation server.

* [app/src/](../app/src/) houses the core web application code.
* [app/src/App/](../app/src/App/) contains the base application logic for IFRC GO, including authentication handling, routing, and React context setup.
Expand Down Expand Up @@ -56,4 +56,10 @@ The [packages/go-ui-storybook](../packages/go-ui-storybook/) directory contains

The [nginx-serve](../nginx-serve) directory contains nginx config and helm charts to run the web application.

> NOTE: We need to add more information later.
### Submodules

The repository uses the following git submodules:

* [go-api/](../go-api/) contains the OpenAPI schema for the GO backend API. Used to generate TypeScript types via `openapi-typescript`.
* [go-risk-module-api/](../go-risk-module-api/) contains the OpenAPI schema for the GO risk module API.
* [cacheppuccino/](../cacheppuccino/) contains the OpenAPI schema for the cacheppuccino translation caching service.
14 changes: 13 additions & 1 deletion collaborating/technology.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,16 @@ The IFRC GO web application is built using [React](https://react.dev/) and [Vite
* **Routing**
* [React Router](https://www.npmjs.com/package/react-router-dom) for client-side routing

* **Excel Exports**
* **Rich Text Editing**
* [TinyMCE](https://www.tiny.cloud/) via [tinymce-react](https://www.npmjs.com/package/@tinymce/tinymce-react) for rich text input fields

* **Business Intelligence**
* [powerbi-client](https://www.npmjs.com/package/powerbi-client) for embedding Power BI reports

* **Excel and CSV I/O**
* [exceljs](https://www.npmjs.com/package/exceljs) for generating and importing Excel files
* [xlsx](https://www.npmjs.com/package/xlsx) for reading Excel files
* [papaparse](https://www.npmjs.com/package/papaparse) for parsing CSV files

* **Data Fetching**
* [@togglecorp/toggle-request](https://www.npmjs.com/package/@togglecorp/toggle-request), a hooks-based request library
Expand All @@ -33,5 +41,9 @@ The IFRC GO web application is built using [React](https://react.dev/) and [Vite
* [Playwright](https://playwright.dev/) for end-to-end testing
* [Vitest](https://vitest.dev/) for unit testing

* **Translation**
* [translatte](../app/scripts/translatte/) — custom CLI tooling for managing translation strings, generating migration files, and syncing with the translation server
* [cacheppuccino](../cacheppuccino/) — translation caching service (git submodule); provides an OpenAPI schema used to generate TypeScript types for translation API calls

* **Versioning and Changelog**
* [Changesets](https://github.com/changesets/changesets) for managing versioning and generating changelogs
63 changes: 62 additions & 1 deletion collaborating/translation.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,65 @@

The IFRC GO application supports the four official languages of the IFRC: English, French, Spanish, and Arabic.

TODO: Add documentation regarding translations
### Overview

Translation strings are stored in `i18n.json` files co-located with each component or view (e.g. `app/src/views/Home/i18n.json`). These files are the source of truth for all translatable strings. A custom CLI tool called **translatte** (located at `app/scripts/translatte/`) manages the full lifecycle of translations: linting, generating migration files, and pushing strings to the translation server.

At runtime the application fetches translations from the **cacheppuccino** service, a translation caching service whose endpoint is configured via the `APP_TRANSLATION_API_ENDPOINT` environment variable.

### Adding or Changing Strings

1. Edit the relevant `i18n.json` file alongside the component.
2. Lint the translation files to catch duplicates or formatting errors:

```bash
pnpm translatte lint ./src/**/i18n.json
```

3. Generate a migration file that captures the diff between the current strings and the last recorded state:

```bash
pnpm translatte generate-migration ./src/translationMigrations ./src/**/i18n.json
```

Or using the convenience script:

```bash
pnpm translatte:generate
```

4. Commit the new migration file alongside your code changes.

### Translatte CLI Reference

All commands are run from within the `app/` workspace directory.

| Command | Description |
|---|---|
| `translatte lint <TRANSLATION_FILE..>` | Lint `i18n.json` files for duplicated strings. Pass `--fix` to auto-fix. |
| `translatte lint-migrations <MIGRATION_DIR_PATH>` | Lint migration files for diverging migrations. |
| `translatte list-migrations <MIGRATION_FILE_PATH>` | List all migration files. |
| `translatte generate-migration <MIGRATION_FILE_PATH> <TRANSLATION_FILE..>` | Generate a new migration file from current translation files. |
| `translatte merge-migrations <MIGRATION_FILE_PATH> --from <file> --to <file>` | Merge a range of migration files into one. |
| `translatte apply-migrations <MIGRATION_FILE_PATH> --source <file> --destination <file>` | Apply pending migrations to a strings JSON file. |
| `translatte export-migration-to-excel <MIGRATION_FILE_PATH> <OUTPUT_DIR>` | Export a migration file to XLSX for external translators. |
| `translatte push-strings-from-excel <IMPORT_FILE_PATH> --api-url <url> --auth-token <token>` | Push translated strings from an XLSX file to the GO API. |
| `translatte push-strings-from-excel-to-ifrc <IMPORT_FILE_PATH> --api-url <url> --api-key <key> --application-id <id>` | Push translated strings from an XLSX file to the IFRC translation service. |
| `translatte push-migrations-to-go <MIGRATION_DIR_PATH> --api-url <url> --auth-token <token>` | Push pending migrations directly to the GO API. |
| `translatte push-migrations-to-ifrc <MIGRATION_DIR_PATH> --api-url <url> --api-key <key> --application-id <id>` | Push pending migrations to the IFRC translation service. |
| `translatte export-server-strings <API_URL>` | Export current server strings to an XLSX file. |
| `translatte clear-server-strings --api-url <url> --auth-token <token>` | Remove all existing strings from the server. |

### Environment Variables

| Variable | Required | Description |
|---|---|---|
| `APP_TRANSLATION_API_ENDPOINT` | Yes | Base URL for the cacheppuccino translation caching service. |

### Translation Workflow (Deployment)

1. Developer adds/changes strings and generates a migration file (`translatte generate-migration`).
2. Migration file is committed and merged into `develop`.
3. During deployment, migrations are applied to produce an updated strings JSON (`translatte apply-migrations`).
4. The updated strings are pushed to the server (`translatte push-migrations-to-go` or `translatte push-migrations-to-ifrc`).
5. Translators receive an XLSX export (`translatte export-migration-to-excel`), translate the new strings, and the translated file is pushed back (`translatte push-strings-from-excel-to-ifrc`).
6 changes: 2 additions & 4 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,11 @@
"dependencies": {
"@ifrc-go/icons": "^2.0.1",
"@togglecorp/fujs": "^2.1.1",
"jiti": "^2.5.1",
"react-focus-on": "^3.9.1",
"sanitize-html": "^2.11.0"
},
"peerDependencies": {
"@ifrc-go/icons": "^1.3.1",
"@ifrc-go/icons": "^2.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
Expand Down Expand Up @@ -91,9 +90,8 @@
"postcss": "^8.5.3",
"postcss-nested": "^7.0.2",
"postcss-normalize": "^13.0.1",
"jiti": "^2.5.1",
"postcss-preset-env": "^10.1.5",
"react-focus-on": "^3.9.1",
"sanitize-html": "^2.11.0",
"stylelint": "^16.17.0",
"stylelint-config-concentric": "^2.0.2",
"stylelint-config-recommended": "^15.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/ChartContainer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { _cs } from '@togglecorp/fujs';

import styles from './styles.module.css';

interface Props {
export interface Props {
className?: string;
children: React.ReactNode;
chartData: {
Expand Down
10 changes: 8 additions & 2 deletions packages/ui/src/components/Checkbox/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { useCallback } from 'react';
import {
useCallback,
useId,
} from 'react';
import { _cs } from '@togglecorp/fujs';

import InputError from '../InputError';
Expand Down Expand Up @@ -64,6 +67,7 @@ function Checkbox<const NAME>(props: Props<NAME>) {
[name, onChange, invertedLogic],
);

const inputId = useId();
const checked = invertedLogic ? !value : value;

const className = _cs(
Expand All @@ -77,12 +81,14 @@ function Checkbox<const NAME>(props: Props<NAME>) {
);

return (
<label // eslint-disable-line jsx-a11y/label-has-associated-control
<label
className={className}
title={tooltip}
htmlFor={inputId}
>
<div className={_cs(styles.checkmarkContainer, checkmarkContainerClassName)}>
<input
id={inputId}
onChange={handleChange}
className={_cs(styles.input, inputClassName)}
type="checkbox"
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/src/components/DropdownMenu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ function DropdownMenu(props: Props) {
colorVariant={labelColorVariant}
withoutPadding={labelWithoutPadding}
spacing={labelSpacing}
aria-expanded={showDropdown}
aria-haspopup="menu"
after={hasAfterContent ? (
<>
{labelAfter}
Expand All @@ -159,6 +161,7 @@ function DropdownMenu(props: Props) {
)}
parentRef={buttonRef}
preferredWidth={preferredPopupWidth}
role="menu"
>
{children}
</Popup>
Expand Down
11 changes: 9 additions & 2 deletions packages/ui/src/components/Modal/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
useCallback,
useEffect,
useId,
} from 'react';
import { FocusOn } from 'react-focus-on';
import { CloseFillIcon } from '@ifrc-go/icons';
Expand Down Expand Up @@ -53,6 +54,7 @@ function Modal(props: Props) {
} = props;

const strings = useTranslation(i18n);
const headingId = useId();

useEffect(
() => {
Expand Down Expand Up @@ -80,6 +82,8 @@ function Modal(props: Props) {
}
}, [onClose, closeOnEscape]);

const { heading, ...restContainerProps } = containerProps;

return (
<Portal>
<div className={_cs(styles.overlay, overlayClassName)}>
Expand All @@ -88,11 +92,14 @@ function Modal(props: Props) {
onClickOutside={handleClickOutside}
onEscapeKey={handleEscape}
gapMode="padding"
// gapMode={null}
role="dialog"
aria-modal="true"
aria-labelledby={heading ? headingId : undefined}
>
<Container
// eslint-disable-next-line react/jsx-props-no-spreading
{...containerProps}
{...restContainerProps}
heading={heading ? <span id={headingId}>{heading}</span> : undefined}
withPadding
withoutWrapInHeader
withoutWrapInFooter
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/Pager/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ function Pager(props: Props) {
onClick={onActivePageChange}
disabled={activePage >= numPages || disabled}
className={styles.pageButton}
title={`${strings.rawButtonGoToPage} ${activePage - 1}`}
title={`${strings.rawButtonGoToPage} ${activePage + 1}`}
>
<ChevronRightLineIcon className={styles.icon} />
</RawButton>
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/src/components/Popup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface Props {
parentRef: React.RefObject<HTMLElement | undefined>;
children?: React.ReactNode;
preferredWidth?: number;
role?: React.AriaRole;
}

function Popup(props: Props) {
Expand All @@ -22,6 +23,7 @@ function Popup(props: Props) {
className,
pointerClassName,
preferredWidth,
role,
} = props;

const {
Expand All @@ -44,6 +46,7 @@ function Popup(props: Props) {
orientation.vertical === 'bottom' && styles.topOrientation,
className,
)}
role={role}
>
{children}
</div>
Expand Down
4 changes: 4 additions & 0 deletions packages/ui/src/components/ProgressBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ function ProgressBar(props: Props) {
&& colorVariantToClassName[colorVariant],
className,
)}
role="progressbar"
aria-valuenow={percentage}
aria-valuemin={0}
aria-valuemax={100}
>
{(title || showPercentageInTitle) && (
<div className={styles.title}>
Expand Down
1 change: 0 additions & 1 deletion packages/ui/src/components/TabLayout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,6 @@ function TabLayout(props: Props) {
isFirstStep && styles.firstStep,
isLastStep && styles.lastStep,
)}
role="tab"
>
<div className={styles.dotWrapper}>
<div className={styles.beforeLine} />
Expand Down
2 changes: 2 additions & 0 deletions packages/ui/src/components/Tabs/Tab/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ export default function Tab<NAME extends TabKey>(props: Props<NAME>) {
name={name}
disabled={disabled}
type="button"
role="tab"
aria-selected={isActive}
>
<TabLayout
// eslint-disable-next-line react/jsx-props-no-spreading
Expand Down
Loading
Loading