1
0
mirror of https://gitlab.nic.cz/turris/reforis/foris-js.git synced 2024-11-14 17:35:35 +01:00

Compare commits

..

2 Commits

Author SHA1 Message Date
Aleksandr Gumroian
065a45dfcc Merge branch 'add-tanstack-and-richtable-component' into 'dev'
Add RichTable component

See merge request turris/reforis/foris-js!253
2024-11-07 21:19:09 +01:00
Aleksandr Gumroian
a3417b58b4
Add RichTable component with header, body, and pagination 2024-11-07 20:57:03 +01:00
11 changed files with 39 additions and 266 deletions

View File

@ -16,11 +16,6 @@ import { Modal, ModalHeader, ModalBody, ModalFooter } from "../bootstrap/Modal";
import { useAlert } from "../context/alertContext/AlertContext";
import { ForisURLs } from "../utils/forisUrls";
RebootButton.propTypes = {
/** Additional props to be passed to the button */
props: PropTypes.object,
};
function RebootButton(props) {
const [triggered, setTriggered] = useState(false);
const [modalShown, setModalShown] = useState(false);
@ -73,12 +68,7 @@ function RebootModal({ shown, setShown, onReboot }) {
<p>{_("Are you sure you want to restart the router?")}</p>
</ModalBody>
<ModalFooter>
<Button
className="btn-secondary"
onClick={() => setShown(false)}
>
{_("Cancel")}
</Button>
<Button onClick={() => setShown(false)}>{_("Cancel")}</Button>
<Button className="btn-danger" onClick={onReboot}>
{_("Confirm reboot")}
</Button>

View File

@ -1,24 +0,0 @@
RebootButton component is a button that opens a modal dialog to confirm the
reboot of the device.
## Usage
```jsx
import React, { useEffect, createContext } from "react";
import RebootButton from "./RebootButton";
import { AlertContextProvider } from "../context/alertContext/AlertContext";
window.AlertContext = React.createContext();
const RebootButtonExample = () => {
return (
<AlertContextProvider>
<div id="modal-container" />
<div id="alert-container" />
<RebootButton />
</AlertContextProvider>
);
};
<RebootButtonExample />;
```

View File

@ -14,7 +14,6 @@ import {
getPaginationRowModel,
useReactTable,
} from "@tanstack/react-table";
import PropTypes from "prop-types";
import RichTableBody from "./RichTableBody";
import RichTableHeader from "./RichTableHeader";
@ -22,28 +21,15 @@ import RichTablePagination from "./RichTablePagination";
const fallbackData = [];
RichTable.propTypes = {
/** Columns to be displayed in the table */
columns: PropTypes.array.isRequired,
/** Data to be displayed in the table */
data: PropTypes.array.isRequired,
/** Whether to display pagination */
withPagination: PropTypes.bool,
/** Number of rows per page */
pageSize: PropTypes.number,
/** Index of the current page */
pageIndex: PropTypes.number,
};
function RichTable({
const RichTable = ({
columns,
data,
withPagination,
pageSize = 5,
pageIndex = 0,
}) {
const tableColumns = useMemo(() => columns, [columns]);
const [tableData] = useState(data ?? fallbackData);
}) => {
const tableColumns = useMemo(() => columns, []);
const [tableData, _] = useState(data ?? fallbackData);
const [sorting, setSorting] = useState([]);
const [pagination, setPagination] = useState({
pageIndex,
@ -79,6 +65,6 @@ function RichTable({
)}
</div>
);
}
};
export default RichTable;

View File

@ -1,135 +0,0 @@
### Description
Rich Table is a table component based on
[Tanstack React Table](https://tanstack.com/table/). It adds some features to
the table component, such as:
- **Pagination**: The table can be paginated.
- **Sorting**: The table can be sorted by columns.
- **Row Expansion**: The table rows can be expanded. (To be implemented)
### Example
```js
import RichTable from "./RichTable";
const columns = [
{
header: "Name",
accessorKey: "name",
},
{
header: "Surname",
accessorKey: "surname",
},
{
header: "Age",
accessorKey: "age",
},
{
header: "Phone",
accessorKey: "phone",
},
];
const data = [
{
name: "John",
surname: "Coltrane",
age: 30,
phone: "123456789",
},
{
name: "Jane",
surname: "Doe",
age: 25,
phone: "987654321",
},
{
name: "Alice",
surname: "Smith",
age: 35,
phone: "123456789",
},
{
name: "Bob",
surname: "Smith",
age: 40,
phone: "987654321",
},
{
name: "Charlie",
surname: "Brown",
age: 45,
phone: "123456789",
},
{
name: "Daisy",
surname: "Brown",
age: 50,
phone: "987654321",
},
{
name: "Eve",
surname: "Johnson",
age: 55,
phone: "123456789",
},
{
name: "Frank",
surname: "Johnson",
age: 60,
phone: "987654321",
},
{
name: "Grace",
surname: "Williams",
age: 65,
phone: "123456789",
},
{
name: "Henry",
surname: "Williams",
age: 70,
phone: "987654321",
},
{
name: "Ivy",
surname: "Brown",
age: 75,
phone: "123456789",
},
{
name: "Jack",
surname: "Brown",
age: 80,
phone: "987654321",
},
{
name: "Kelly",
surname: "Johnson",
age: 85,
phone: "123456789",
},
{
name: "Liam",
surname: "Johnson",
age: 90,
phone: "987654321",
},
{
name: "Mia",
surname: "Williams",
age: 95,
phone: "123456789",
},
{
name: "Nathan",
surname: "Williams",
age: 100,
phone: "987654321",
},
];
<RichTable columns={columns} data={data} withPagination />;
```

View File

@ -7,16 +7,7 @@
import React from "react";
import propTypes from "prop-types";
RichTableBody.propTypes = {
table: propTypes.shape({
getRowModel: propTypes.func.isRequired,
}).isRequired,
flexRender: propTypes.func.isRequired,
};
function RichTableBody({ table, flexRender }) {
const RichTableBody = ({ table, flexRender }) => {
return (
<tbody>
{table.getRowModel().rows.map((row) => {
@ -43,6 +34,6 @@ function RichTableBody({ table, flexRender }) {
})}
</tbody>
);
}
};
export default RichTableBody;

View File

@ -6,30 +6,21 @@
*/
import React from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faSquareCaretUp,
faSquareCaretDown,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import propTypes from "prop-types";
RichTableHeader.propTypes = {
table: propTypes.shape({
getHeaderGroups: propTypes.func.isRequired,
}).isRequired,
flexRender: propTypes.func.isRequired,
};
function RichTableHeader({ table, flexRender }) {
const getThTitle = (header) => {
if (!header.column.getCanSort()) return undefined;
const nextSortingOrder = header.column.getNextSortingOrder();
if (nextSortingOrder === "asc") return _("Sort ascending");
if (nextSortingOrder === "desc") return _("Sort descending");
return _("Clear sort");
};
const RichTableHeader = ({ table, flexRender }) => {
const getThTitle = (header) =>
header.column.getCanSort()
? header.column.getNextSortingOrder() === "asc"
? _("Sort ascending")
: header.column.getNextSortingOrder() === "desc"
? _("Sort descending")
: _("Clear sort")
: undefined;
return (
<thead className="thead-light">
@ -54,7 +45,6 @@ function RichTableHeader({ table, flexRender }) {
</div>
) : (
<button
type="button"
className={`btn btn-link text-decoration-none text-reset fw-bold p-0 d-flex align-items-center
${
header.column.getCanSort()
@ -91,6 +81,6 @@ function RichTableHeader({ table, flexRender }) {
))}
</thead>
);
}
};
export default RichTableHeader;

View File

@ -6,33 +6,15 @@
*/
import React, { useMemo } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faAngleLeft,
faAnglesLeft,
faAngleRight,
faAnglesRight,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import propTypes from "prop-types";
RichTablePagination.propTypes = {
table: propTypes.shape({
getState: propTypes.func.isRequired,
getCanPreviousPage: propTypes.func.isRequired,
getCanNextPage: propTypes.func.isRequired,
firstPage: propTypes.func.isRequired,
previousPage: propTypes.func.isRequired,
nextPage: propTypes.func.isRequired,
lastPage: propTypes.func.isRequired,
setPageSize: propTypes.func.isRequired,
getPageCount: propTypes.func.isRequired,
}).isRequired,
tablePageSize: propTypes.number,
allRows: propTypes.number,
};
function RichTablePagination({ table, tablePageSize, allRows }) {
const RichTablePagination = ({ table, tablePageSize, allRows }) => {
const { pagination } = table.getState();
const prevPagBtnDisabled = !table.getCanPreviousPage();
const nextPagBtnDisabled = !table.getCanNextPage();
@ -49,7 +31,6 @@ function RichTablePagination({ table, tablePageSize, allRows }) {
style={{ cursor: disabled ? "not-allowed" : "pointer" }}
>
<button
type="button"
className="page-link"
aria-label={ariaLabel}
onClick={onClick}
@ -123,6 +104,6 @@ function RichTablePagination({ table, tablePageSize, allRows }) {
</select>
</nav>
);
}
};
export default RichTablePagination;

View File

@ -43,7 +43,7 @@ exports[`<RebootButton/> Render modal. 1`] = `
class="modal-footer"
>
<button
class="btn btn-secondary d-inline-flex justify-content-center align-items-center"
class="btn btn-primary d-inline-flex justify-content-center align-items-center"
type="button"
>
<span>

View File

@ -14,7 +14,7 @@ import { render } from "@testing-library/react";
import PropTypes from "prop-types";
import { AlertContextMock } from "./alertContextMock";
import { CustomizationContextMock } from "./customizationContextMock";
import { CustomizationContextMock } from "./cutomizationContextMock";
Wrapper.propTypes = {
children: PropTypes.oneOfType([

View File

@ -28,11 +28,11 @@ module.exports = {
content: "docs/development.md",
},
{
name: "Common Components",
name: "Components",
description: "Set of main components.",
sections: [
{
name: "Foris Form",
name: "Foris forms",
components: [
"src/form/components/ForisForm.js",
"src/form/components/alerts.js",
@ -42,22 +42,25 @@ module.exports = {
usageMode: "expand",
},
{
name: "Rich Table",
components: ["src/common/RichTable/RichTable.js"],
exampleMode: "expand",
usageMode: "expand",
},
{
name: "Reboot Button",
components: ["src/common/RebootButton.js"],
name: "Alert Context",
components: ["src/context/alertContext/AlertContext.js"],
exampleMode: "expand",
usageMode: "expand",
},
],
sectionDepth: 1,
},
{
name: "Bootstrap Components",
name: "Customization Context",
components: [
"src/context/customizationContext/CustomizationContext.js",
],
exampleMode: "expand",
usageMode: "expand",
},
{
name: "Bootstrap components",
description: "Set of bootstrap components.",
components: "src/bootstrap/*.js",
exampleMode: "expand",
@ -65,22 +68,13 @@ module.exports = {
ignore: ["src/bootstrap/constants.js", "src/bootstrap/Radio.js"],
sectionDepth: 0,
},
{
name: "Contexts",
components: [
"src/context/alertContext/AlertContext.js",
"src/context/customizationContext/CustomizationContext.js",
],
exampleMode: "expand",
usageMode: "expand",
},
],
template: {
favicon: "/docs/components/logo.svg",
},
require: [
"babel-polyfill",
path.join(__dirname, "src/testUtils/mockGlobals.js"),
path.join(__dirname, "src/testUtils/mockGlobals"),
path.join(
__dirname,
"node_modules/bootstrap/dist/css/bootstrap.min.css"