Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
ab6c279
feat(unit-formatter): add % formatter
DanCouper Jul 21, 2021
a86edf2
feat: create two new packages to expand functionality of existing
DanCouper Jul 22, 2021
68a8ae8
fix: remove obselete timeDisplayPref property from UserPreferences in…
DanCouper Jul 22, 2021
fb92d8f
chore(root manifest): remove aliases for deprecated packages, add ali…
DanCouper Jul 22, 2021
af31777
chore: version bumps to major for affected packages
DanCouper Jul 22, 2021
b3b6a69
chore: update README for data-transformers with formatter info
DanCouper Jul 22, 2021
c08b806
Merge branch 'develop' into 45/unit_formatter_not_reponding_to_pref_c…
DanCouper Jul 22, 2021
16739a5
fix: remove @ prefix from storage keys
DanCouper Jul 23, 2021
238799b
fix(auth-flows): expose inClearingCurrentPin state in provider
DanCouper Jul 26, 2021
887f341
Merge branch 'develop' into 45/unit_formatter_not_reponding_to_pref_c…
DanCouper Jul 26, 2021
dbce0a6
Merge branch 'develop' into 45/unit_formatter_not_reponding_to_pref_c…
DanCouper Jul 26, 2021
0398fce
fix: merge conflict in autogenerated yarn versions file
DanCouper Jul 26, 2021
47d98af
chore: update README for both new packages
DanCouper Jul 26, 2021
2b991b8
chore(data-transformers): add formatter usage example to README
DanCouper Jul 26, 2021
0880bb2
chore(application-configuration): add more reasoning info to README
DanCouper Jul 26, 2021
57ba123
chore(application-configuration): remove shared types dependency
DanCouper Jul 26, 2021
494e374
chore(application-configuration): minor version bump
DanCouper Jul 26, 2021
3138d7f
chore(data-transformers): minor version bump
DanCouper Jul 26, 2021
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
2 changes: 2 additions & 0 deletions .yarn/versions/63a48cdd.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
undecided:
- "@thingco/application-configuration"
- "@thingco/auth-flows"
- "@thingco/graphviz"
- "@thingco/graphviz-native"
- "@thingco/graphviz-web"
- "@thingco/react-component-library"
- "@thingco/shared-types"
- "@thingco/unit-formatter"
- "@thingco/user-preferences"
- "@thingco/user-preferences-store-native"
- "@thingco/user-preferences-store-web"
Expand Down
17 changes: 8 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@
"scripts": {
"packages:build-all": "FORCE_COLOR=1 yarn workspaces foreach -v --topological-dev --exclude @thingco/shared-frontend-libs-playground run build",
"packages:test-all": "yarn workspaces foreach --exclude @thingco/shared-frontend-libs-playground run test",
"package:application-configuration": "yarn workspace @thingco/application-configuration",
"package:auth-flows": "yarn workspace @thingco/auth-flows",
"package:data-transformers": "yarn workspace @thingco/data-transformers",
"package:graphviz": "yarn workspace @thingco/graphviz",
"package:graphviz-web": "yarn workspace @thingco/graphviz-web",
"package:graphviz-native": "yarn workspace @thingco/graphviz-native",
"package:react-component-library": "yarn workspace @thingco/react-component-library",
"package:shared-types": "yarn workspace @thingco/shared-types",
"package:unit-formatter": "yarn workspace @thingco/unit-formatter",
"package:user-preferences": "yarn workspace @thingco/user-preferences",
"package:user-preferences-store-web": "yarn workspace @thingco/user-preferences-store-web",
"package:user-preferences-store-native": "yarn workspace @thingco/user-preferences-store-native",
"playground": "yarn workspace @thingco/shared-frontend-libs-playground",
Expand All @@ -32,20 +31,20 @@
"devDependencies": {
"@types/eslint": "^7.28.0",
"@types/eslint-plugin-prettier": "^3.1.0",
"@types/node": "^16.3.2",
"@types/node": "^16.4.3",
"@types/prettier": "^2.3.2",
"@types/rimraf": "^3.0.1",
"@typescript-eslint/eslint-plugin": "^4.28.3",
"@typescript-eslint/parser": "^4.28.3",
"esbuild": "^0.12.15",
"eslint": "^7.30.0",
"@typescript-eslint/eslint-plugin": "^4.28.4",
"@typescript-eslint/parser": "^4.28.4",
"esbuild": "^0.12.16",
"eslint": "^7.31.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-react": "^7.24.0",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-tsdoc": "^0.2.14",
"husky": "^4.3.8",
"lint-staged": "^11.0.1",
"lint-staged": "^11.1.1",
"prettier": "^2.3.2",
"rimraf": "^3.0.2",
"typescript": "^4.3.5",
Expand Down
244 changes: 244 additions & 0 deletions packages/application-configuration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
# `@thingco/application-configuration`

## Overview

An React/React Native interface for storing application config and user preferences for ThingCo apps.

Provides a provider and two hooks:

`ApplicationConfigProvider` stores both build-time configs that we need to access within the app
(there are read-only), and user preferences, which modifiable and are persisted to device/browser storage.

```
<ApplicationConfigProvider configs={...buildTimeConfigs} store={userPreferencesStorageInterface}>
...rest of app
</ApplicationConfigProvider>
```

Because of what it contains, it needs to sit at the top of the app, everything else
feeds off it.

`useConfiguration` is a hook that allows access to configuration values -- it returns a plain object with
primitives for all values.

`usePreferences` is a hook that allows read and write access to persisted user preferences -- it returns an
object with two properties: the preferences themselves are a plain object under the key `prefs`, and the `setPref` method takes the key of a specific preference + new value and sets it both locally an in persisted
storage.

## Why use a provider for this?

There are three reasons:

1. `usePreferences` functionality is already in the app -- this package simply moves that API into this package -- and is required for reading/writing user preferences.
2. `useConfiguration` functionality moves configs into one single place in the app: this means that there is a centralised place to write them to at build time rather than multiple places. Following on from this:
3. It becomes easier to test, as there is a single provider to mock to inject configuration values into integration tests -- any components making use of configurations can just use a mock version of this provider, rather than having to fiddle around mocking disparate imports.

> **NOTE** currently only one config value is present -- aim would be to push all relevant values into the provider. This also opens up possibility of getting them asynchronously from (_eg_) an S3 bucket that contains all configs used by a given client.

## Installation

Ensure your project is set up to allow pulling `@thingco/` namespaced packages from GitHub rather than NPM.

```
yarn add @thingco/config-provider
```

It is necessary to provide a store implementation. You can build one yourself -- see the respective `user-preferences-store-` implementations as an example, they're very simple. But more likely
you will want to just use the web storage API or the React Native API. For web, you'll need React installed, then:

```
yarn add @thingco/user-preferences-store-web
```

For React native, you'll need React, React Native and AsyncStorage (`@react-native-async-storage/async-storage`) installed. Then:

```
yarn add @thingco/user-preferences-store-native
```

## Setup

Import both the provider and the store. **Note** if you are on native, install AsyncStorage: it is required as a peer dependency.

```javascript
import { UserPrefsProvider } from "@thingco/user-preferences";
import { createPrefStore } from "@thingco/user-preferences-store-{native|web}";
```

Create the store. This will leverage localStorage on the web and AsyncStorage on native:

```javascript
const store = createPrefStore({
distanceUnitPref: "km",
distanceUnitPrecisionPref: 1,
localePref: "en-GB",
timeDisplayPref: "24",
});
```

You need to pass specific values for the config: like the preferences, these are typed, so
arbitrary values cannot be passed:

```javascript
const configs = {
distanceUntilScored: 160934,
};
```

Then add the provider at the very top of the component tree:

```jsx
<ApplicationConfigProvider configs={configs} store={store}>
{/* ...snip */}
{children}
{/* ...snip */}
</UserPrefsProvider>
```

You can now get and set the user preferences using the `usePreferences` hook, and get the configs by using the `useConfig` hook.

## Usage

### Available preferences

> **I'm using Typescript. Why can't I change what the user preferences actually are (or, say, add to them)‽**
>
> The preferences are typed: they aren't freeform, you cannot just add arbitrary preference keys/values. This is by design.
>
> To amend them, go to the `types` file in this repo and make your adjustments there. This library doesn't care what the actual preferences are as long as they match what is defined in that package.
>
> **NOTE** that adding/removing/changing the preferences **will** require rebuilding of all dependent packages,
> followed by a version bump for each, followed by republishing. Packages affected are:
>
> 1. This package, `@thingco/application-configuration`
> 2. The auth package, `@thingco/auth-flows`
> 3. The two preferences store packages, `@thingco/user-preferences-store-{native|web}`
> 4. The data transformations package, `@thingco/data-transformers`

### Hooks

#### `useConfig`

##### Interface

```typescript
interface ApplicationConfiguration {
/**
* A distance (in metres) that represents the distance travelled will trigger
* creation of a new block. By default this will be 160934, _ie_ one mile in metres
*/
distanceUntilScored: number;
}
```

##### Usage

```typescript
function useConfig(): ApplcationConfig;
```

The `useConfig` hook returns an object containing all properties passed into the provider in the first place.

To _read_ the configs in components:

```typescript
const MyComponent = () => {
const { distanceUntilScored } = useConfig();

return (
<dl>
<dt>The distance in metres until a block score is complete and a score can be calculated:</dt>
<dd>{distanceUntilScored}</dd>
</dl>
);
};
```

---

#### `usePreferences`

##### Interface

```typescript
interface UserPreferences {
/**
* Imperial or Si, so miles or kilometres (`"mi"` or `"km"`)
*/
distanceUnit: DistanceUnitPreference;
/**
* The precision used to display distance units. Can be passed to formatting functions
* to round the displayed distance to the specified precision.
*
* Note that a precision of 0 will round to the nearest integer value.
*/
distanceUnitPrecision: number;
/**
* The locale used for formatting. Note that this is currently set as the Unicode
* organisation's BCP 47 scheme of language tags, using the "modern" set.
*
* Taken from https://github.com/unicode-org/cldr-json/blob/master/cldr-json/cldr-core/availableLocales.json
*/
locale: LocalePreference;
/**
* Whether to show time in 12 or 24 hour format, so "12" or "24". Note that this
* is a string -- it's a key, not a number.
*/
timeDisplay: TimeDisplayPreference;
}
```

##### Usage

```typescript
function usePreferences(): {
prefs: UserPreferences;
setPref: <K extends keyof UserPreferences>(key: K, value: UserPreferences[K]) => void;
};
```

The `usePrefs` hook returns an object with two properties: `prefs`, which is the `UserPreferences` object described above, and `setPref`, which is a function that accepts one of the object keys + a new value, and updates the value in the provider, saving it to the defined store as a side effect.

To _read_ preferences in components:

```jsx
const MyComponent = () => {
const { prefs } = usePreferences();

return (
<dl>
<dt>Distance unit:</dt>
<dd>{prefs.distanceUnitPref}</dd>

<dt>Distance unit precision:</dt>
<dd>{prefs.distanceUnitPrecisionPref}</dd>

<dt>Locale:</dt>
<dd>{prefs.localePref}</dd>

<dt>Time display (12 or 24 hour):</dt>
<dd>{prefs.timeDisplayPref}</dd>
</dl>
);
};
```

To _write_ a preference:

```jsx
const MyComponent = () => {
const { prefs, setPref } = usePreferences();

const toggleDistancePref = () => {
const currentPref = prefs.distanceUnitPref;
setPref("distanceUnitPref", currentPref === "km" ? "mi" : "km");
};

return (
<div>
<p>Unit for distance is {prefs.distanceUnitPref}</p>
<button onClick={toggleDistancePref}>Toggle pref</button>
</div>
);
};
```
14 changes: 14 additions & 0 deletions packages/application-configuration/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
globals: {
"ts-jest": {
tsconfig: "tsconfig.jest.json",
},
},
roots: ["<rootDir>/src"],
testEnvironment: "jsdom",
testMatch: ["**/__tests__/**/*.+(ts|tsx|js)", "**/?(*.)+(spec|test).+(ts|tsx|js)"],
transform: {
"^.+\\.(ts|tsx)$": "ts-jest",
},
verbose: true,
};
39 changes: 39 additions & 0 deletions packages/application-configuration/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "@thingco/application-configuration",
"description": "User preference storage/access and application configuration access for ThingCo React apps",
"version": "1.1.0",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
"files": [
"lib/**"
],
"public": true,
"publishConfig": {
"registry": "https://npm.pkg.github.com/"
},
"repository": "https://github.com/thingco/shared-frontend-libs",
"scripts": {
"build": "yarn global:build",
"test": "yarn jest",
"docs": "yarn rimraf docs && yarn typedoc"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^12.0.0",
"@types/jest": "^26.0.24",
"@types/react": "^17.0.15",
"@types/react-dom": "^17.0.9",
"@types/rimraf": "^3.0.1",
"@types/testing-library__jest-dom": "^5.14.1",
"jest": "^27.0.6",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"rimraf": "^3.0.2",
"ts-jest": "^27.0.4",
"typedoc": "^0.21.4",
"typescript": "^4.3.5"
},
"peerDependencies": {
"react": "*"
}
}
Loading