mirror of
https://gitlab.nic.cz/turris/reforis/foris-js.git
synced 2024-12-26 00:21:36 +01:00
Merge branch 'dev' into 'master'
Release 1.1.0 See merge request turris/reforis/foris-js!24
This commit is contained in:
commit
8f88b09e66
|
@ -6,7 +6,7 @@
|
|||
|
||||
Each commit to `dev` branch will result in publishing a new version of library
|
||||
tagged `beta`. Versions names are based on commit SHA, e.g.
|
||||
`foris@0.1.0-d9073aa4.0`.
|
||||
`foris@0.1.0-beta.d9073aa4`.
|
||||
|
||||
### Preparing a release
|
||||
|
||||
|
|
2
package-lock.json
generated
2
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "foris",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "foris",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"description": "Set of components and utils for Foris and its plugins.",
|
||||
"author": "CZ.NIC, z.s.p.o.",
|
||||
"repository": {
|
||||
|
|
|
@ -6,11 +6,12 @@ then
|
|||
exit 1
|
||||
else
|
||||
# Need to replace "_" with "_" as GitLab CI won't accept secret vars with "-"
|
||||
echo "//registry.npmjs.org/:_authToken=$(echo $NPM_TOKEN | tr _ -)" > .npmrc
|
||||
echo "//registry.npmjs.org/:_authToken=$(echo "$NPM_TOKEN" | tr _ -)" > .npmrc
|
||||
echo "unsafe-perm = true" >> ~/.npmrc
|
||||
if test "$1" = "beta"
|
||||
then
|
||||
npm version prerelease --preid=$CI_COMMIT_SHORT_SHA --git-tag-version false
|
||||
BETA_VERSION=$(npx -c 'echo "$npm_package_version"')-beta.$CI_COMMIT_SHORT_SHA
|
||||
npm version "$BETA_VERSION" --git-tag-version false
|
||||
npm publish --tag beta
|
||||
elif test "$1" = "latest"
|
||||
then
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
API_ACTIONS, TIMEOUT, HEADERS, APIReducer, getErrorMessage,
|
||||
} from "./utils";
|
||||
|
||||
export function useAPIPost(url) {
|
||||
export function useAPIPost(url, contentType) {
|
||||
const [state, dispatch] = useReducer(APIReducer, {
|
||||
isSending: false,
|
||||
isError: false,
|
||||
|
@ -20,12 +20,17 @@ export function useAPIPost(url) {
|
|||
data: null,
|
||||
});
|
||||
|
||||
const headers = { ...HEADERS };
|
||||
if (contentType) {
|
||||
headers["Content-Type"] = contentType;
|
||||
}
|
||||
|
||||
const post = async (data) => {
|
||||
dispatch({ type: API_ACTIONS.INIT });
|
||||
try {
|
||||
const result = await axios.post(url, data, {
|
||||
timeout: TIMEOUT,
|
||||
headers: HEADERS,
|
||||
headers,
|
||||
});
|
||||
dispatch({ type: API_ACTIONS.SUCCESS, payload: result.data });
|
||||
} catch (error) {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { useUID } from "react-uid/dist/es5/index";
|
||||
import { useUID } from "react-uid";
|
||||
|
||||
import { formFieldsSize } from "./constants";
|
||||
|
||||
|
|
21
src/bootstrap/DownloadButton.js
Normal file
21
src/bootstrap/DownloadButton.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
|
||||
*
|
||||
* This is free software, licensed under the GNU General Public License v3.
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
DownloadButton.propTypes = {
|
||||
href: PropTypes.string.isRequired,
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
]),
|
||||
};
|
||||
|
||||
export function DownloadButton({ href, children }) {
|
||||
return <a href={href} className="btn btn-primary" download>{children}</a>;
|
||||
}
|
7
src/bootstrap/DownloadButton.md
Normal file
7
src/bootstrap/DownloadButton.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
Hyperlink with apperance of a button.
|
||||
|
||||
It has `download` attribute, which prevents closing WebSocket connection on Firefox. See [related issue](https://bugzilla.mozilla.org/show_bug.cgi?id=858538) for more details.
|
||||
|
||||
```js
|
||||
<DownloadButton href="example.zip">Download</DownloadButton>
|
||||
```
|
34
src/bootstrap/FileInput.js
Normal file
34
src/bootstrap/FileInput.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
|
||||
*
|
||||
* This is free software, licensed under the GNU General Public License v3.
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
|
||||
import PropTypes from "prop-types";
|
||||
import { Input } from "./Input";
|
||||
|
||||
FileInput.propTypes = {
|
||||
/** Field label. */
|
||||
label: PropTypes.string.isRequired,
|
||||
/** Error message. */
|
||||
error: PropTypes.string,
|
||||
/** Help text message. */
|
||||
helpText: PropTypes.string,
|
||||
/** Email value. */
|
||||
value: PropTypes.string,
|
||||
};
|
||||
|
||||
export function FileInput({ ...props }) {
|
||||
return (
|
||||
<Input
|
||||
type="file"
|
||||
className="custom-file-input"
|
||||
labelClassName="custom-file-label"
|
||||
groupClassName="custom-file"
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
15
src/bootstrap/FileInput.md
Normal file
15
src/bootstrap/FileInput.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
Bootstrap component for file input. Includes label and has predefined sizes and structure for using in foris forms.
|
||||
|
||||
All additional `props` are passed to the `<input type="file">` HTML component.
|
||||
|
||||
```js
|
||||
import {useState} from 'react';
|
||||
const [files, setFiles] = useState([]);
|
||||
|
||||
<FileInput
|
||||
files={files}
|
||||
label="Some file"
|
||||
helpText="Will be uploaded"
|
||||
onChange={event =>setFiles(event.target.files)}
|
||||
/>
|
||||
```
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import React from "react";
|
||||
import { useUID } from "react-uid/dist/es5/index";
|
||||
import { useUID } from "react-uid";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { formFieldsSize } from "./constants";
|
||||
|
@ -21,31 +21,31 @@ Input.propTypes = {
|
|||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
]),
|
||||
labelClassName: PropTypes.string,
|
||||
groupClassName: PropTypes.string,
|
||||
};
|
||||
|
||||
/** Base bootstrap input component. */
|
||||
export function Input({
|
||||
type, label, helpText, error, className, children, ...props
|
||||
type, label, helpText, error, className, children, labelClassName, groupClassName, ...props
|
||||
}) {
|
||||
const uid = useUID();
|
||||
const inputClassName = `form-control ${className || ""} ${(error ? "is-invalid" : "")}`.trim();
|
||||
return (
|
||||
<div className={formFieldsSize}>
|
||||
<div className="form-group">
|
||||
<label htmlFor={uid}>{label}</label>
|
||||
<div className="input-group">
|
||||
<input
|
||||
className={inputClassName}
|
||||
type={type}
|
||||
id={uid}
|
||||
<div className={`form-group ${formFieldsSize}`}>
|
||||
<label className={labelClassName} htmlFor={uid}>{label}</label>
|
||||
<div className={`input-group ${groupClassName || ""}`.trim()}>
|
||||
<input
|
||||
className={inputClassName}
|
||||
type={type}
|
||||
id={uid}
|
||||
|
||||
{...props}
|
||||
/>
|
||||
{children}
|
||||
</div>
|
||||
{error ? <div className="invalid-feedback">{error}</div> : null}
|
||||
{helpText ? <small className="form-text text-muted">{helpText}</small> : null}
|
||||
{...props}
|
||||
/>
|
||||
{children}
|
||||
</div>
|
||||
{error ? <div className="invalid-feedback">{error}</div> : null}
|
||||
{helpText ? <small className="form-text text-muted">{helpText}</small> : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { useUID } from "react-uid/dist/es5/index";
|
||||
import { useUID } from "react-uid";
|
||||
|
||||
import { formFieldsSize } from "./constants";
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { useUID } from "react-uid/dist/es5/index";
|
||||
import { useUID } from "react-uid";
|
||||
|
||||
|
||||
Select.propTypes = {
|
||||
|
|
19
src/bootstrap/__tests__/DownloadButton.test.js
Normal file
19
src/bootstrap/__tests__/DownloadButton.test.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
|
||||
*
|
||||
* This is free software, licensed under the GNU General Public License v3.
|
||||
* See /LICENSE for more information.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
|
||||
import { render } from "customTestRender";
|
||||
|
||||
import { DownloadButton } from "../DownloadButton";
|
||||
|
||||
describe("<DownloadButton />", () => {
|
||||
it("should have download attribute", () => {
|
||||
const { container } = render(<DownloadButton href="http://example.com">Test Button</DownloadButton>);
|
||||
expect(container.firstChild.getAttribute("download")).not.toBeNull();
|
||||
});
|
||||
});
|
|
@ -2,53 +2,49 @@
|
|||
|
||||
exports[`<NumberInput/> Render number input 1`] = `
|
||||
<div
|
||||
class="col-sm-12 offset-lg-1 col-lg-10"
|
||||
class="form-group col-sm-12 offset-lg-1 col-lg-10"
|
||||
>
|
||||
<div
|
||||
class="form-group"
|
||||
<label
|
||||
for="1"
|
||||
>
|
||||
<label
|
||||
for="1"
|
||||
>
|
||||
Test label
|
||||
</label>
|
||||
Test label
|
||||
</label>
|
||||
<div
|
||||
class="input-group"
|
||||
>
|
||||
<input
|
||||
class="form-control"
|
||||
id="1"
|
||||
type="number"
|
||||
value="1"
|
||||
/>
|
||||
<div
|
||||
class="input-group"
|
||||
class="input-group-append"
|
||||
>
|
||||
<input
|
||||
class="form-control"
|
||||
id="1"
|
||||
type="number"
|
||||
value="1"
|
||||
/>
|
||||
<div
|
||||
class="input-group-append"
|
||||
<button
|
||||
aria-label="Increase"
|
||||
class="btn btn-outline-secondary"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
aria-label="Increase"
|
||||
class="btn btn-outline-secondary"
|
||||
type="button"
|
||||
>
|
||||
<i
|
||||
class="fas fa-plus"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
aria-label="Decrease"
|
||||
class="btn btn-outline-secondary"
|
||||
type="button"
|
||||
>
|
||||
<i
|
||||
class="fas fa-minus"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<i
|
||||
class="fas fa-plus"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
aria-label="Decrease"
|
||||
class="btn btn-outline-secondary"
|
||||
type="button"
|
||||
>
|
||||
<i
|
||||
class="fas fa-minus"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<small
|
||||
class="form-text text-muted"
|
||||
>
|
||||
Some help text
|
||||
</small>
|
||||
</div>
|
||||
<small
|
||||
class="form-text text-muted"
|
||||
>
|
||||
Some help text
|
||||
</small>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -2,32 +2,28 @@
|
|||
|
||||
exports[`<PasswordInput/> Render password input 1`] = `
|
||||
<div
|
||||
class="col-sm-12 offset-lg-1 col-lg-10"
|
||||
class="form-group col-sm-12 offset-lg-1 col-lg-10"
|
||||
>
|
||||
<div
|
||||
class="form-group"
|
||||
<label
|
||||
for="1"
|
||||
>
|
||||
<label
|
||||
for="1"
|
||||
>
|
||||
Test label
|
||||
</label>
|
||||
<div
|
||||
class="input-group"
|
||||
>
|
||||
<input
|
||||
autocomplete="new-password"
|
||||
class="form-control"
|
||||
id="1"
|
||||
type="password"
|
||||
value="Some password"
|
||||
/>
|
||||
</div>
|
||||
<small
|
||||
class="form-text text-muted"
|
||||
>
|
||||
Some help text
|
||||
</small>
|
||||
Test label
|
||||
</label>
|
||||
<div
|
||||
class="input-group"
|
||||
>
|
||||
<input
|
||||
autocomplete="new-password"
|
||||
class="form-control"
|
||||
id="1"
|
||||
type="password"
|
||||
value="Some password"
|
||||
/>
|
||||
</div>
|
||||
<small
|
||||
class="form-text text-muted"
|
||||
>
|
||||
Some help text
|
||||
</small>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -2,31 +2,27 @@
|
|||
|
||||
exports[`<TextInput/> Render text input 1`] = `
|
||||
<div
|
||||
class="col-sm-12 offset-lg-1 col-lg-10"
|
||||
class="form-group col-sm-12 offset-lg-1 col-lg-10"
|
||||
>
|
||||
<div
|
||||
class="form-group"
|
||||
<label
|
||||
for="1"
|
||||
>
|
||||
<label
|
||||
for="1"
|
||||
>
|
||||
Test label
|
||||
</label>
|
||||
<div
|
||||
class="input-group"
|
||||
>
|
||||
<input
|
||||
class="form-control"
|
||||
id="1"
|
||||
type="text"
|
||||
value="Some text"
|
||||
/>
|
||||
</div>
|
||||
<small
|
||||
class="form-text text-muted"
|
||||
>
|
||||
Some help text
|
||||
</small>
|
||||
Test label
|
||||
</label>
|
||||
<div
|
||||
class="input-group"
|
||||
>
|
||||
<input
|
||||
class="form-control"
|
||||
id="1"
|
||||
type="text"
|
||||
value="Some text"
|
||||
/>
|
||||
</div>
|
||||
<small
|
||||
class="form-text text-muted"
|
||||
>
|
||||
Some help text
|
||||
</small>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -17,7 +17,7 @@ const FORM_ACTIONS = {
|
|||
resetData: 2,
|
||||
};
|
||||
|
||||
export function useForm(validator, prepData) {
|
||||
export function useForm(validator, dataPreprocessor) {
|
||||
const [state, dispatch] = useReducer(formReducer, {
|
||||
data: null,
|
||||
initialData: null,
|
||||
|
@ -28,10 +28,10 @@ export function useForm(validator, prepData) {
|
|||
dispatch({
|
||||
type: FORM_ACTIONS.resetData,
|
||||
data,
|
||||
prepData,
|
||||
dataPreprocessor,
|
||||
validator,
|
||||
});
|
||||
}, [prepData, validator]);
|
||||
}, [dataPreprocessor, validator]);
|
||||
|
||||
const onFormChangeHandler = useCallback((updateRule) => (event) => {
|
||||
dispatch({
|
||||
|
@ -41,6 +41,7 @@ export function useForm(validator, prepData) {
|
|||
validator,
|
||||
});
|
||||
}, [validator]);
|
||||
|
||||
return [
|
||||
state,
|
||||
onFormChangeHandler,
|
||||
|
@ -61,12 +62,15 @@ function formReducer(state, action) {
|
|||
};
|
||||
}
|
||||
case FORM_ACTIONS.resetData: {
|
||||
if (!action.data) return { ...state, initialData: state.data };
|
||||
const prepData = action.prepData ? action.prepData(action.data) : action.data;
|
||||
if (!action.data) {
|
||||
return { ...state, initialData: state.data };
|
||||
}
|
||||
|
||||
const data = action.dataPreprocessor ? action.dataPreprocessor(action.data) : action.data;
|
||||
return {
|
||||
data: prepData,
|
||||
initialData: prepData,
|
||||
errors: action.data ? action.validator(prepData) : undefined,
|
||||
data,
|
||||
initialData: data,
|
||||
errors: action.data ? action.validator(data) : undefined,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
|
@ -82,6 +86,9 @@ function getChangedValue(target) {
|
|||
} else if (target.type === "number") {
|
||||
const parsedValue = parseInt(value);
|
||||
value = Number.isNaN(parsedValue) ? value : parsedValue;
|
||||
} else if (target.type === "file") {
|
||||
// Return first file (we don't need multiple yet)
|
||||
[value] = target.files;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
|
|
@ -15,15 +15,17 @@ export { useAPIPatch } from "api/patch";
|
|||
export { Alert } from "bootstrap/Alert";
|
||||
export { Button } from "bootstrap/Button";
|
||||
export { CheckBox } from "bootstrap/CheckBox";
|
||||
export { formFieldsSize } from "bootstrap/constants";
|
||||
export { DownloadButton } from "bootstrap/DownloadButton";
|
||||
export { DataTimeInput } from "bootstrap/DataTimeInput";
|
||||
export { EmailInput } from "bootstrap/EmailInput";
|
||||
export { FileInput } from "bootstrap/FileInput";
|
||||
export { Input } from "bootstrap/Input";
|
||||
export { NumberInput } from "bootstrap/NumberInput";
|
||||
export { PasswordInput } from "bootstrap/PasswordInput";
|
||||
export { RadioSet } from "bootstrap/RadioSet";
|
||||
export { Select } from "bootstrap/Select";
|
||||
export { TextInput } from "bootstrap/TextInput";
|
||||
export { formFieldsSize } from "bootstrap/constants";
|
||||
|
||||
export {
|
||||
Spinner,
|
||||
|
|
Loading…
Reference in New Issue
Block a user