Skip to content

Commit

Permalink
feat: add ui-components package (#2321)
Browse files Browse the repository at this point in the history
* feat: add NatAmountInput
  • Loading branch information
katelynsills authored Mar 22, 2021
1 parent f5768be commit 770542e
Show file tree
Hide file tree
Showing 28 changed files with 3,363 additions and 26 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/test-all-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,10 @@ jobs:
run: cd packages/deploy-script-support && yarn test
env:
ESM_DISABLE_CACHE: true
- name: yarn test (ui-components)
run: cd packages/ui-components && yarn test
env:
ESM_DISABLE_CACHE: true

##############
# Long-running tests are executed individually.
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,5 @@ bundle-*.js
.idea/

/packages.png
packages/ui-components/compiled
packages/ui-components/dist
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
"packages/deployment",
"packages/notifier",
"packages/xsnap",
"packages/deploy-script-support"
"packages/deploy-script-support",
"packages/ui-components"
],
"devDependencies": {
"@typescript-eslint/parser": "^4.18.0",
Expand Down
24 changes: 24 additions & 0 deletions packages/ui-components/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
.eslintcache
34 changes: 34 additions & 0 deletions packages/ui-components/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# UI Components

Reusable UI Components for [Agoric](https://agoric.com) [Dapps](https://agoric.com/documentation/dapps/), built with [React](https://reactjs.org) and [MaterialUI](https://materialui.com).

## NatAmountInput

A [React](https://reactjs.org) [MaterialUI TextField
Input](https://material-ui.com/api/text-field/) which allows the user
to enter a `Nat`. Handles `decimalPlaces` appropriately. This is a
controlled component.

Example:

```
import { NatAmountInput } from '@agoric/ui-components';
<NatAmountInput
label={label} // the label
value={amount && amount.value} // The value to display. Must be a Nat
decimalPlaces={purse.displayInfo && purse.displayInfo.decimalPlaces}
placesToShow={2}
disabled={disabled} // disable the input
error={amountError} // any error to display
onChange={onAmountChange} // a callback called on user input changing the value
onError={() => {}} // a callback called on errors
/>
```

## Yarn Test

```sh
yarn build
yarn test
```
3 changes: 3 additions & 0 deletions packages/ui-components/babel.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-react"]
}
Empty file.
19 changes: 19 additions & 0 deletions packages/ui-components/jsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// This file can contain .js-specific Typescript compiler config.
{
"compilerOptions": {
"target": "esnext",

"noEmit": true,
/*
// The following flags are for creating .d.ts files:
"noEmit": false,
"declaration": true,
"emitDeclarationOnly": true,
*/
"downlevelIteration": true,
"strictNullChecks": true,
"moduleResolution": "node",
"jsx": "react",
},
"include": ["src/**/*.js", "test/**/*.js", "exported.js"],
}
108 changes: 108 additions & 0 deletions packages/ui-components/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
{
"name": "@agoric/ui-components",
"version": "0.0.1",
"description": "Reusable UI Components for Agoric Dapps, built with React and MaterialUI",
"main": "src/index.js",
"peerDependencies": {
"@agoric/assert": "^0.2.3",
"@agoric/ertp": "^0.10.0",
"@agoric/eventual-send": "^0.13.3",
"@agoric/install-ses": "^0.5.3",
"@material-ui/core": "^4.11.3",
"react": "^16.14.0",
"react-dom": "^16.8.0"
},
"scripts": {
"test": "BABEL_ENV='test' ava",
"build:tests": "rm -rf compiled && BABEL_ENV='test' ./node_modules/.bin/babel test/components --out-dir compiled/test/components",
"build:src": "rm -rf dist && BABEL_ENV='test' ./node_modules/.bin/babel src --out-dir dist",
"build": "yarn build:src && yarn build:tests",
"lint-fix": "yarn lint --fix",
"lint": "yarn lint:types && yarn lint:eslint",
"lint-check": "yarn lint",
"lint:eslint": "eslint '**/*.js'",
"lint:types": "tsc -p jsconfig.json"
},
"eslintConfig": {
"extends": [
"react-app",
"@agoric"
],
"rules": {
"no-use-before-define": "off",
"@typescript-eslint/no-use-before-define": [
"error"
]
}
},
"eslintIgnore": [
"dist",
"compiled"
],
"prettier": {
"trailingComma": "all",
"singleQuote": true
},
"devDependencies": {
"@babel/cli": "^7.12.13",
"@babel/core": "^7.12.13",
"@babel/plugin-syntax-jsx": "^7.12.1",
"@material-ui/core": "4.11.3",
"@typescript-eslint/eslint-plugin": "^4.14.2",
"ava": "^3.15.0",
"babel-eslint": "^10.1.0",
"babel-plugin-named-asset-import": "^0.3.7",
"babel-preset-react-app": "^10.0.0",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.6",
"eslint-config-react-app": "^6.0.0",
"eslint-plugin-flowtype": "^5.2.0",
"eslint-plugin-react": "^7.22.0",
"eslint-plugin-react-hooks": "^4.2.0",
"esm": "^3.2.25",
"prettier": "^1.19.0",
"react": "^16.14.0",
"react-dom": "^16.8.0",
"typescript": "^4.2.3"
},
"ava": {
"files": [
"compiled/test/components/**/test-*.js",
"test/**/*.js",
"!test/components"
],
"require": [
"esm",
"./test/_setup-enzyme-adapter.js"
]
},
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Agoric/agoric-sdk.git"
},
"keywords": [
"smart",
"contract",
"cryptocurrency",
"exchange",
"tokens"
],
"author": "Agoric",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/Agoric/agoric-sdk/issues"
},
"homepage": "https://github.com/Agoric/agoric-sdk#readme",
"files": [
"src",
"dist",
"NEWS.md",
"exported.js"
],
"dependencies": {
"@agoric/nat": "^4.0.0"
}
}
46 changes: 46 additions & 0 deletions packages/ui-components/src/components/NatAmountInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from 'react';
import { TextField } from '@material-ui/core';

import { parseAsNat } from '../display/natValue/parseAsNat';
import { stringifyNat } from '../display/natValue/stringifyNat';

// https://material-ui.com/api/text-field/

const NatAmountInput = ({
label,
value,
decimalPlaces = 0,
placesToShow = 2,
disabled,
error,
onChange,
}) => {
// No negative values allowed in the input
const noNegativeValues = {
inputProps: { min: 0 },
};

const preventSubtractChar = e => {
if (e.key === 'Subtract') {
e.preventDefault();
e.stopPropagation();
}
};

return (
<TextField
label={label}
type="number"
variant="outlined"
fullWidth
InputProps={noNegativeValues}
onChange={ev => onChange(parseAsNat(ev.target.value, decimalPlaces))}
onKeyPress={preventSubtractChar}
value={stringifyNat(value, decimalPlaces, placesToShow)}
disabled={disabled}
error={error}
/>
);
};

export default NatAmountInput;
111 changes: 111 additions & 0 deletions packages/ui-components/src/display/display.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// @ts-check
import { assert, details } from '@agoric/assert';
import { MathKind } from '@agoric/ertp';
import '@agoric/ertp/exported';

import { parseAsNat } from './natValue/parseAsNat';
import { stringifyNat } from './natValue/stringifyNat';
import { parseAsSet } from './setValue/parseAsSet';
import { stringifySet } from './setValue/stringifySet';

/**
*
* @param {string} str - string to parse as a value
* @param {AmountMathKind} [mathKind] - mathKind of the value
* @param {number} [decimalPlaces] - places to move the decimal to the left
* @returns {Value}
*/
export const parseAsValue = (
str,
mathKind = MathKind.NAT,
decimalPlaces = 0,
) => {
if (mathKind === MathKind.NAT) {
return parseAsNat(str, decimalPlaces);
}
if (mathKind === MathKind.SET) {
return parseAsSet(str);
}
assert.fail(details`MathKind ${mathKind} must be NAT or SET`);
};

/**
* @param {string} str - string to parse as a value
* @param {Brand} brand - brand to use in the amount
* @param {AmountMathKind} [mathKind] - mathKind of the value
* @param {number} [decimalPlaces] - places to move the decimal to the left
* @returns {Amount}
*/
export const parseAsAmount = (
str,
brand,
mathKind = MathKind.NAT,
decimalPlaces = 0,
) => {
return { brand, value: parseAsValue(str, mathKind, decimalPlaces) };
};

/**
*
* @param {Value} value - value to stringify
* @param {AmountMathKind} [mathKind] - mathKind of the value
* @param {number} [decimalPlaces] - places to move the decimal to the
* right in the string
* @param {number} [placesToShow] - places after the decimal to show
* @returns {string}
*/
export const stringifyValue = (
value,
mathKind = MathKind.NAT,
decimalPlaces = 0,
placesToShow = 2,
) => {
if (mathKind === MathKind.NAT) {
// @ts-ignore Value is a Nat
return stringifyNat(value, decimalPlaces, placesToShow);
}
if (mathKind === MathKind.SET) {
// @ts-ignore Value is a SetValue
return stringifySet(value);
}
assert.fail(details`MathKind ${mathKind} must be NAT or SET`);
};

/**
* Stringify the value of a purse
*
* @param {any} purse
* @returns {string}
*/
export const stringifyPurseValue = purse => {
if (!purse) {
return '0';
}
return stringifyValue(
purse.value,
purse.displayInfo.mathKind,
purse.displayInfo.decimalPlaces,
);
};

/**
* Stringify the value in an amount
*
* @param {Amount} amount
* @param {AmountMathKind} [mathKind] - mathKind of the value
* @param {number} [decimalPlaces] - places to move the decimal to the
* right in the string
* @param {number} [placesToShow] - places after the decimal to show
* @returns {string}
*/
export function stringifyAmountValue(
amount,
mathKind,
decimalPlaces,
placesToShow,
) {
if (!amount) {
return '0';
}
return stringifyValue(amount.value, mathKind, decimalPlaces, placesToShow);
}
Loading

0 comments on commit 770542e

Please sign in to comment.