Skip to content

Commit

Permalink
feat(convert-translation): adds bin for translation files (#2)
Browse files Browse the repository at this point in the history
* feat(convert-translation): adds bin for translation files

* test(ConfigProvider): settings

* ci: test and build workflow
  • Loading branch information
mstrk authored Apr 19, 2021
1 parent 739463b commit 92841b9
Show file tree
Hide file tree
Showing 16 changed files with 4,677 additions and 110 deletions.
20 changes: 20 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ version: 2.1
orbs:
node: circleci/[email protected]
jobs:
test-and-build:
executor:
name: node/default
steps:
- checkout
- node/with-cache:
steps:
- run: npm ci
- run: npm run build

build-and-publish:
executor:
name: node/default
Expand All @@ -12,7 +22,17 @@ jobs:
- run: npm ci
- run: npm run build
- run: npm run semantic-release

workflows:
test-and-build:
jobs:
- test-and-build:
filters:
branches:
ignore:
- next
- master

build-and-publish:
jobs:
- build-and-publish:
Expand Down
10 changes: 8 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"rules": {
"semi": ["error", "always"],
"quotes": ["error", "double", { "avoidEscape": true }],
"indent": ["error", 2],
"indent": ["error", 2, { "SwitchCase": 1 }],
"prefer-const": "error",
"no-trailing-spaces": "error",
"space-before-blocks": ["error", "always"],
Expand All @@ -42,13 +42,19 @@
},
"overrides": [
{
"files": ["./scripts/*"],
"files": ["./scripts/*", "./__tests__/__mocks__/jest/*"],
"env": {
"node": true
},
"rules": {
"@typescript-eslint/no-var-requires": "off"
}
},
{
"files": ["./__tests__/*"],
"env": {
"jest": true
}
}
]
}
42 changes: 39 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ npm install @apisuite/fe-base

### ConfigProvider

Editor provider, should wrap all other components.
The provider should wrap all other components.

| prop | required | example value | description |
|------|----------|-------|-------------|
| api | Yes | `{ base: <ROOT_ENDPOINT>, settings?: <SETTINGS_ROUTE>, owner?: <OWNER_ROUTE> }` | API endpoints to fetch settings. Only base is required, the other routes will default to it's property names. |
| translations | Yes | `{ "en-US": import("./en-US"), "pt-PT": import("./pt-PT") }` | Dynamic imports mapping for translations . |

Wrap your App with the `ConfigProvider` to have access to the settings and style context.
Have access to settings and style context. Wrap your App with the `ConfigProvider`.

```tsx
const translations = {
Expand All @@ -39,7 +39,6 @@ const translations = {
Using settings:

```tsx
import React, { useEffect } from "react";
import { useConfig } from "@apisuite/fe-base";

export const MyComponent = () => {
Expand Down Expand Up @@ -126,6 +125,43 @@ export const MyComponent = () => {
};
```
## Tools
### convert-translation
A `convert-translation` script is provided to help convert `json` translation files into `csv` format and back.
To use it run it from the bin folder `./node_modules/.bin/convert-translation` with a file path as argument or add it to your scripts:
```json
...
"scripts": {
...
"convert-translation": "./node_modules/.bin/convert-translation"
}
...
```
Then in your terminal run:
```bash
npm run convert-translation ./translations/en-US.json # npm run convert-translation ./en-US.csv
```
**JSON File**
Normal `[key]: value` pairs object.
**CSV File**
The CSV file follows google translation service requirements, which consists in two columns without a header, where the first is the `key` and the second is the `value`. You can add more columns to give more context to the translators, `convert-translation` will only handle this two columns.
| | |
|-|-|
| MyComponent.welcome | welcome to {{portalName}}! |
| MyComponent.registerCTA | register |
| MyPage.hello | Hello World! |
<a name="release-irrelevant"></a>
<br />
Expand Down
41 changes: 41 additions & 0 deletions __tests__/ConfigProvider.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import "jest-fetch-mock";
import React from "react";
import { render, screen, waitFor } from "./test-utils";
import { DisplaySettings } from "./__mocks__/components/DisplaySettings";

import settingsMock from "./__mocks__/settings.json";
import ownerMock from "./__mocks__/owner.json";

// Reset any runtime request handlers we may add during the tests.
beforeEach(() => {
fetchMock.resetMocks();
});

describe("ConfigProvider", () => {
describe("settings", () => {
test("Renders info from useConfig", async () => {
fetchMock.mockResponses(
[JSON.stringify(settingsMock), { status: 200 }],
[JSON.stringify(ownerMock), { status: 200 }],
);

await waitFor(() => {
render(<DisplaySettings />);
});

const portalName = await screen.findByTestId("portal-name");
const ownerName = await screen.findByTestId("owner-name");

expect(portalName.innerHTML).toBe(settingsMock.portalName);
expect(ownerName.innerHTML).toBe(ownerMock.name);
});
});

describe("i18n", () => {
// TODO: test translations
});

describe("style system", () => {
// TODO: test style system
});
});
13 changes: 13 additions & 0 deletions __tests__/__mocks__/components/DisplaySettings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from "react";
import { useConfig } from "../../../lib";

export function DisplaySettings() {
const { portalName, ownerInfo } = useConfig();

return (
<div>
<div data-testid="portal-name">{portalName}</div>
<div data-testid="owner-name">{ownerInfo.name}</div>
</div>
);
}
1 change: 1 addition & 0 deletions __tests__/__mocks__/jest/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require("jest-fetch-mock").enableMocks();
5 changes: 5 additions & 0 deletions __tests__/__mocks__/jest/transformStub.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
process: function() {
return "";
},
};
17 changes: 17 additions & 0 deletions __tests__/__mocks__/owner.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"id": 1,
"description": null,
"logo": null,
"name": "ACME",
"org_code": "ba8d729f-a8ad",
"privacyUrl": null,
"supportUrl": null,
"terms": null,
"tosUrl": null,
"vat": null,
"website": "http://acme.com",
"websiteUrl": null,
"youtubeUrl": null,
"createdAt": "2021-02-24T00:00:00.000Z",
"updatedAt": "2021-02-24T00:00:00.000Z"
}
89 changes: 89 additions & 0 deletions __tests__/__mocks__/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
{
"clientName": "acme",
"portalName": "acme",
"providerSignupURL": "singlesignon.com",
"sso": ["keycloak"],
"i18nOptions": [
{"locale": "en-US", "label": "We speak English"},
{"locale": "pt-PT", "label": "Nós falamos Português"}
],
"theme": {
"type": "light",
"common": {
"black": "#000000",
"white": "#FFFFFF"
},
"text": {
"primary": "#14283C",
"secondary": "#85909A",
"disabled": "#DBDEE1",
"hint": "#51606E"
},
"primary": {
"main": "#14283C",
"dark": "#000017",
"light": "#374858",
"contrastText": "#FFFFFF"
},
"secondary": {
"main": "#32C896",
"dark": "#007C45",
"light": "#B3E7D1",
"contrastText": "#14283C"
},
"background": {
"default": "#F7F8F9",
"paper": "#FFFFFF"
},
"info": {
"main": "#19B3EE",
"dark": "#035E86",
"light": "#BBECFF"
},
"success": {
"main": "#14DE2D",
"dark": "#037C17",
"light": "#A9F19E"
},
"warning": {
"dark": "#80460B",
"light": "#FFDCB9",
"main": "#F78E27"
},
"error": {
"main": "#FF1515",
"dark": "#840608",
"light": "#FFA1A1"
},
"action": {
"active": "#51606E",
"disabled": "#DBDEE1",
"disabledBackground": "rgba(0, 0, 0, 0.12)",
"focus": "#19B3EE",
"hover": "rgba(0,0,0,0.04)",
"selected": "rgba(0,0,0,0.08)"
},
"divider": "#DBDEE1",
"label": "#BAC0C6",
"grey": {
"50": "#F7F8F9",
"100": "#ECEDEF",
"200": "#DBDEE1",
"300": "#BAC0C6",
"400": "#85909A",
"500": "#6A7783",
"600": "#646464",
"700": "#535E68",
"800": "#515151",
"900": "#131313"
},
"gradient": {
"main": "linear-gradient(270deg, #C8DC8C 0%, #007D7D 100%)",
"dark": "linear-gradient(270deg, #7DD291 0%, #007D7D 100%)",
"light": "linear-gradient(270deg, #C8DC8C 0%, #19A38A 100%)"
},
"dimensions": {
"borderRadius": 4
}
}
}
25 changes: 25 additions & 0 deletions __tests__/test-utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { FC, ReactElement } from "react";
import { render, RenderOptions } from "@testing-library/react";
import { ConfigProvider } from "../lib";

function mockImport() {
return Promise.resolve({});
}

const Provider: FC = (props) => {
return (
<ConfigProvider
api={{ base: "https://example.com" }}
translations={{ "en-US": mockImport() }}
{...props}
/>
);
};

const customRender = (
ui: ReactElement,
options?: RenderOptions
) => render(ui, { wrapper: Provider, ...options });

export * from "@testing-library/react";
export { customRender as render };
22 changes: 22 additions & 0 deletions jest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{

"automock": false,
"preset": "ts-jest",
"testRegex": "__tests__/.*.spec.(js|ts|tsx)$",
"moduleFileExtensions": ["ts", "tsx", "js", "json", "node"],
"moduleDirectories": ["lib", "node_modules"],
"transform": {
"^.+\\.ts$": "ts-jest",
".+\\.(jpg|jpeg|png|gif|svg|graphql)$": "<rootDir>/__tests__/__mocks__/jest/transformStub.js"
},
"setupFiles": [
"./__tests__/__mocks__/jest/setup.js"
],
"collectCoverage": false,
"collectCoverageFrom": [
"**/*.{ts,tsx}",
"!**/index.{ts,tsx}",
"!**/util/test-utils.tsx",
"!**/node_modules/**"
]
}
Loading

0 comments on commit 92841b9

Please sign in to comment.