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
|
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.
|
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
|
### Preparing a release
|
||||||
|
|
||||||
|
|
2
package-lock.json
generated
2
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "foris",
|
"name": "foris",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "foris",
|
"name": "foris",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"description": "Set of components and utils for Foris and its plugins.",
|
"description": "Set of components and utils for Foris and its plugins.",
|
||||||
"author": "CZ.NIC, z.s.p.o.",
|
"author": "CZ.NIC, z.s.p.o.",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
|
@ -6,11 +6,12 @@ then
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
# Need to replace "_" with "_" as GitLab CI won't accept secret vars with "-"
|
# 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
|
echo "unsafe-perm = true" >> ~/.npmrc
|
||||||
if test "$1" = "beta"
|
if test "$1" = "beta"
|
||||||
then
|
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
|
npm publish --tag beta
|
||||||
elif test "$1" = "latest"
|
elif test "$1" = "latest"
|
||||||
then
|
then
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {
|
||||||
API_ACTIONS, TIMEOUT, HEADERS, APIReducer, getErrorMessage,
|
API_ACTIONS, TIMEOUT, HEADERS, APIReducer, getErrorMessage,
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
|
|
||||||
export function useAPIPost(url) {
|
export function useAPIPost(url, contentType) {
|
||||||
const [state, dispatch] = useReducer(APIReducer, {
|
const [state, dispatch] = useReducer(APIReducer, {
|
||||||
isSending: false,
|
isSending: false,
|
||||||
isError: false,
|
isError: false,
|
||||||
|
@ -20,12 +20,17 @@ export function useAPIPost(url) {
|
||||||
data: null,
|
data: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const headers = { ...HEADERS };
|
||||||
|
if (contentType) {
|
||||||
|
headers["Content-Type"] = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
const post = async (data) => {
|
const post = async (data) => {
|
||||||
dispatch({ type: API_ACTIONS.INIT });
|
dispatch({ type: API_ACTIONS.INIT });
|
||||||
try {
|
try {
|
||||||
const result = await axios.post(url, data, {
|
const result = await axios.post(url, data, {
|
||||||
timeout: TIMEOUT,
|
timeout: TIMEOUT,
|
||||||
headers: HEADERS,
|
headers,
|
||||||
});
|
});
|
||||||
dispatch({ type: API_ACTIONS.SUCCESS, payload: result.data });
|
dispatch({ type: API_ACTIONS.SUCCESS, payload: result.data });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { useUID } from "react-uid/dist/es5/index";
|
import { useUID } from "react-uid";
|
||||||
|
|
||||||
import { formFieldsSize } from "./constants";
|
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 React from "react";
|
||||||
import { useUID } from "react-uid/dist/es5/index";
|
import { useUID } from "react-uid";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
import { formFieldsSize } from "./constants";
|
import { formFieldsSize } from "./constants";
|
||||||
|
@ -21,31 +21,31 @@ Input.propTypes = {
|
||||||
PropTypes.arrayOf(PropTypes.node),
|
PropTypes.arrayOf(PropTypes.node),
|
||||||
PropTypes.node,
|
PropTypes.node,
|
||||||
]),
|
]),
|
||||||
|
labelClassName: PropTypes.string,
|
||||||
|
groupClassName: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Base bootstrap input component. */
|
/** Base bootstrap input component. */
|
||||||
export function Input({
|
export function Input({
|
||||||
type, label, helpText, error, className, children, ...props
|
type, label, helpText, error, className, children, labelClassName, groupClassName, ...props
|
||||||
}) {
|
}) {
|
||||||
const uid = useUID();
|
const uid = useUID();
|
||||||
const inputClassName = `form-control ${className || ""} ${(error ? "is-invalid" : "")}`.trim();
|
const inputClassName = `form-control ${className || ""} ${(error ? "is-invalid" : "")}`.trim();
|
||||||
return (
|
return (
|
||||||
<div className={formFieldsSize}>
|
<div className={`form-group ${formFieldsSize}`}>
|
||||||
<div className="form-group">
|
<label className={labelClassName} htmlFor={uid}>{label}</label>
|
||||||
<label htmlFor={uid}>{label}</label>
|
<div className={`input-group ${groupClassName || ""}`.trim()}>
|
||||||
<div className="input-group">
|
<input
|
||||||
<input
|
className={inputClassName}
|
||||||
className={inputClassName}
|
type={type}
|
||||||
type={type}
|
id={uid}
|
||||||
id={uid}
|
|
||||||
|
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
|
||||||
{error ? <div className="invalid-feedback">{error}</div> : null}
|
|
||||||
{helpText ? <small className="form-text text-muted">{helpText}</small> : null}
|
|
||||||
</div>
|
</div>
|
||||||
|
{error ? <div className="invalid-feedback">{error}</div> : null}
|
||||||
|
{helpText ? <small className="form-text text-muted">{helpText}</small> : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { useUID } from "react-uid/dist/es5/index";
|
import { useUID } from "react-uid";
|
||||||
|
|
||||||
import { formFieldsSize } from "./constants";
|
import { formFieldsSize } from "./constants";
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import { useUID } from "react-uid/dist/es5/index";
|
import { useUID } from "react-uid";
|
||||||
|
|
||||||
|
|
||||||
Select.propTypes = {
|
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`] = `
|
exports[`<NumberInput/> Render number input 1`] = `
|
||||||
<div
|
<div
|
||||||
class="col-sm-12 offset-lg-1 col-lg-10"
|
class="form-group col-sm-12 offset-lg-1 col-lg-10"
|
||||||
>
|
>
|
||||||
<div
|
<label
|
||||||
class="form-group"
|
for="1"
|
||||||
>
|
>
|
||||||
<label
|
Test label
|
||||||
for="1"
|
</label>
|
||||||
>
|
<div
|
||||||
Test label
|
class="input-group"
|
||||||
</label>
|
>
|
||||||
|
<input
|
||||||
|
class="form-control"
|
||||||
|
id="1"
|
||||||
|
type="number"
|
||||||
|
value="1"
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
class="input-group"
|
class="input-group-append"
|
||||||
>
|
>
|
||||||
<input
|
<button
|
||||||
class="form-control"
|
aria-label="Increase"
|
||||||
id="1"
|
class="btn btn-outline-secondary"
|
||||||
type="number"
|
type="button"
|
||||||
value="1"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
class="input-group-append"
|
|
||||||
>
|
>
|
||||||
<button
|
<i
|
||||||
aria-label="Increase"
|
class="fas fa-plus"
|
||||||
class="btn btn-outline-secondary"
|
/>
|
||||||
type="button"
|
</button>
|
||||||
>
|
<button
|
||||||
<i
|
aria-label="Decrease"
|
||||||
class="fas fa-plus"
|
class="btn btn-outline-secondary"
|
||||||
/>
|
type="button"
|
||||||
</button>
|
>
|
||||||
<button
|
<i
|
||||||
aria-label="Decrease"
|
class="fas fa-minus"
|
||||||
class="btn btn-outline-secondary"
|
/>
|
||||||
type="button"
|
</button>
|
||||||
>
|
|
||||||
<i
|
|
||||||
class="fas fa-minus"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<small
|
|
||||||
class="form-text text-muted"
|
|
||||||
>
|
|
||||||
Some help text
|
|
||||||
</small>
|
|
||||||
</div>
|
</div>
|
||||||
|
<small
|
||||||
|
class="form-text text-muted"
|
||||||
|
>
|
||||||
|
Some help text
|
||||||
|
</small>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -2,32 +2,28 @@
|
||||||
|
|
||||||
exports[`<PasswordInput/> Render password input 1`] = `
|
exports[`<PasswordInput/> Render password input 1`] = `
|
||||||
<div
|
<div
|
||||||
class="col-sm-12 offset-lg-1 col-lg-10"
|
class="form-group col-sm-12 offset-lg-1 col-lg-10"
|
||||||
>
|
>
|
||||||
<div
|
<label
|
||||||
class="form-group"
|
for="1"
|
||||||
>
|
>
|
||||||
<label
|
Test label
|
||||||
for="1"
|
</label>
|
||||||
>
|
<div
|
||||||
Test label
|
class="input-group"
|
||||||
</label>
|
>
|
||||||
<div
|
<input
|
||||||
class="input-group"
|
autocomplete="new-password"
|
||||||
>
|
class="form-control"
|
||||||
<input
|
id="1"
|
||||||
autocomplete="new-password"
|
type="password"
|
||||||
class="form-control"
|
value="Some password"
|
||||||
id="1"
|
/>
|
||||||
type="password"
|
|
||||||
value="Some password"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<small
|
|
||||||
class="form-text text-muted"
|
|
||||||
>
|
|
||||||
Some help text
|
|
||||||
</small>
|
|
||||||
</div>
|
</div>
|
||||||
|
<small
|
||||||
|
class="form-text text-muted"
|
||||||
|
>
|
||||||
|
Some help text
|
||||||
|
</small>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -2,31 +2,27 @@
|
||||||
|
|
||||||
exports[`<TextInput/> Render text input 1`] = `
|
exports[`<TextInput/> Render text input 1`] = `
|
||||||
<div
|
<div
|
||||||
class="col-sm-12 offset-lg-1 col-lg-10"
|
class="form-group col-sm-12 offset-lg-1 col-lg-10"
|
||||||
>
|
>
|
||||||
<div
|
<label
|
||||||
class="form-group"
|
for="1"
|
||||||
>
|
>
|
||||||
<label
|
Test label
|
||||||
for="1"
|
</label>
|
||||||
>
|
<div
|
||||||
Test label
|
class="input-group"
|
||||||
</label>
|
>
|
||||||
<div
|
<input
|
||||||
class="input-group"
|
class="form-control"
|
||||||
>
|
id="1"
|
||||||
<input
|
type="text"
|
||||||
class="form-control"
|
value="Some text"
|
||||||
id="1"
|
/>
|
||||||
type="text"
|
|
||||||
value="Some text"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<small
|
|
||||||
class="form-text text-muted"
|
|
||||||
>
|
|
||||||
Some help text
|
|
||||||
</small>
|
|
||||||
</div>
|
</div>
|
||||||
|
<small
|
||||||
|
class="form-text text-muted"
|
||||||
|
>
|
||||||
|
Some help text
|
||||||
|
</small>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -17,7 +17,7 @@ const FORM_ACTIONS = {
|
||||||
resetData: 2,
|
resetData: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useForm(validator, prepData) {
|
export function useForm(validator, dataPreprocessor) {
|
||||||
const [state, dispatch] = useReducer(formReducer, {
|
const [state, dispatch] = useReducer(formReducer, {
|
||||||
data: null,
|
data: null,
|
||||||
initialData: null,
|
initialData: null,
|
||||||
|
@ -28,10 +28,10 @@ export function useForm(validator, prepData) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: FORM_ACTIONS.resetData,
|
type: FORM_ACTIONS.resetData,
|
||||||
data,
|
data,
|
||||||
prepData,
|
dataPreprocessor,
|
||||||
validator,
|
validator,
|
||||||
});
|
});
|
||||||
}, [prepData, validator]);
|
}, [dataPreprocessor, validator]);
|
||||||
|
|
||||||
const onFormChangeHandler = useCallback((updateRule) => (event) => {
|
const onFormChangeHandler = useCallback((updateRule) => (event) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
|
@ -41,6 +41,7 @@ export function useForm(validator, prepData) {
|
||||||
validator,
|
validator,
|
||||||
});
|
});
|
||||||
}, [validator]);
|
}, [validator]);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
state,
|
state,
|
||||||
onFormChangeHandler,
|
onFormChangeHandler,
|
||||||
|
@ -61,12 +62,15 @@ function formReducer(state, action) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case FORM_ACTIONS.resetData: {
|
case FORM_ACTIONS.resetData: {
|
||||||
if (!action.data) return { ...state, initialData: state.data };
|
if (!action.data) {
|
||||||
const prepData = action.prepData ? action.prepData(action.data) : action.data;
|
return { ...state, initialData: state.data };
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = action.dataPreprocessor ? action.dataPreprocessor(action.data) : action.data;
|
||||||
return {
|
return {
|
||||||
data: prepData,
|
data,
|
||||||
initialData: prepData,
|
initialData: data,
|
||||||
errors: action.data ? action.validator(prepData) : undefined,
|
errors: action.data ? action.validator(data) : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
@ -82,6 +86,9 @@ function getChangedValue(target) {
|
||||||
} else if (target.type === "number") {
|
} else if (target.type === "number") {
|
||||||
const parsedValue = parseInt(value);
|
const parsedValue = parseInt(value);
|
||||||
value = Number.isNaN(parsedValue) ? value : parsedValue;
|
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;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,15 +15,17 @@ export { useAPIPatch } from "api/patch";
|
||||||
export { Alert } from "bootstrap/Alert";
|
export { Alert } from "bootstrap/Alert";
|
||||||
export { Button } from "bootstrap/Button";
|
export { Button } from "bootstrap/Button";
|
||||||
export { CheckBox } from "bootstrap/CheckBox";
|
export { CheckBox } from "bootstrap/CheckBox";
|
||||||
export { formFieldsSize } from "bootstrap/constants";
|
export { DownloadButton } from "bootstrap/DownloadButton";
|
||||||
export { DataTimeInput } from "bootstrap/DataTimeInput";
|
export { DataTimeInput } from "bootstrap/DataTimeInput";
|
||||||
export { EmailInput } from "bootstrap/EmailInput";
|
export { EmailInput } from "bootstrap/EmailInput";
|
||||||
|
export { FileInput } from "bootstrap/FileInput";
|
||||||
export { Input } from "bootstrap/Input";
|
export { Input } from "bootstrap/Input";
|
||||||
export { NumberInput } from "bootstrap/NumberInput";
|
export { NumberInput } from "bootstrap/NumberInput";
|
||||||
export { PasswordInput } from "bootstrap/PasswordInput";
|
export { PasswordInput } from "bootstrap/PasswordInput";
|
||||||
export { RadioSet } from "bootstrap/RadioSet";
|
export { RadioSet } from "bootstrap/RadioSet";
|
||||||
export { Select } from "bootstrap/Select";
|
export { Select } from "bootstrap/Select";
|
||||||
export { TextInput } from "bootstrap/TextInput";
|
export { TextInput } from "bootstrap/TextInput";
|
||||||
|
export { formFieldsSize } from "bootstrap/constants";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Spinner,
|
Spinner,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user