diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c4f747716bf2dc4a9dfe36721fe596cec97cf2a7..db85d4fd65c6fdee8a969f80b8f18384290ade32 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,5 @@ stages: - - linting - test - - analyze - build - release - integration @@ -33,12 +31,14 @@ default: - node --version - npm ci -test_storybook: - stage: test - script: - - npm install - - npx playwright install --with-deps - - npm run test-storybook:ci +#test_storybook: +# stage: test +# needs: [build_static_files] +# image: mcr.microsoft.com/playwright:v1.42.1-jammy +# script: +# - npm install +# - npx playwright install --with-deps +# - npm run test-storybook:ci build_static_files: stage: build diff --git a/README.md b/README.md index 449ab1423881fe55cbfbd4c5f0f6b4c41079a756..982fa85b45ab345398707426f45ff85ee6bb2326 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,9 @@ All additional dependencies should be in the `devDependencies` category. There s #### Test runner +> WARNING: The test runner tests are very slow at the moment. +> They don't add much value as they are right now, so it is not recommended to run them. + When you run tests for the first time or when Playwright is updated, run the following command to install dependencies: diff --git a/package.json b/package.json index 33ae3e9188698201b804a6e551ece8a8ec4f596c..ebc1eee8008bc6131b456ac6bada375fe1579bd1 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "storybook": "concurrently \"npm run storybook:css\" \"storybook dev -p 6006\"", "storybook:css": "tailwindcss -w -i ./src/tailwind.css -o ./src/index.css", "test-storybook": "test-storybook --browsers firefox chromium webkit", - "test-storybook:ci": "concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"npm run build-storybook --quiet && npx http-server storybook-static --port 6006 --silent\" \"wait-on tcp:127.0.0.1:6006 && npm run test-storybook\"", + "test-storybook:ci": "concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"npx http-server storybook-static --port 6006 --silent\" \"wait-on tcp:127.0.0.1:6006 && npm run test-storybook\"", "semantic-release": "semantic-release" }, "lint-staged": { diff --git a/src/components/Table.tsx b/src/components/Table.tsx index 88988241267126a3be346826e468c528716f515a..eeeec422f3087c1a5555650d9534532150eaf3e9 100644 --- a/src/components/Table.tsx +++ b/src/components/Table.tsx @@ -5,6 +5,7 @@ import type { ColumnEditorOptions, ColumnEvent } from "primereact/column"; import { clsx } from "clsx"; import { textColor } from "./utils/classes.ts"; import Typography from "./Typography.tsx"; +import {NoResults} from "./EmptyState.tsx"; type ColumnProps = { field: string; @@ -25,9 +26,13 @@ type TableProps = { isReorderable?: boolean; onReorder?: Dispatch<SetStateAction<DataTableValueArray>>; editMode?: "cell" | "row"; - emptyMessage?: string; + emptyMessage?: ReactNode; }; +const defaultEmptyMessage = ( + <div className="my-4"><NoResults title="" content={<Typography text="No results found." variant="paragraph" />} /> </div> +) + // todo pagination, expand rows (in design system but not needed in proposal tool yet) const Table = ({ columns, @@ -39,7 +44,7 @@ const Table = ({ isReorderable = false, onReorder, editMode, - emptyMessage + emptyMessage = defaultEmptyMessage }: TableProps) => { return ( <DataTable diff --git a/src/stories/Table.stories.tsx b/src/stories/Table.stories.tsx index e49cbbef256c5db2a9c2759b07122714f8dd0849..1a7acb10e697eb52ada0cdd2bd0ce7fe255d9824 100644 --- a/src/stories/Table.stories.tsx +++ b/src/stories/Table.stories.tsx @@ -63,7 +63,7 @@ const rows = [ name: "Tony", role: "CEO", status: <Badge text="Active" />, - action: <Button label="Edit" color="secondary" icon="edit" />, + action: <Button label="Edit" color="secondary" icon="Edit" />, }, { key: "2", @@ -72,8 +72,8 @@ const rows = [ status: <Badge text="Paused" color="warning" />, action: ( <div className="flex flex-row gap-3 w-fit"> - <Button label="Edit" color="secondary" icon="edit" /> - <Button label="Delete" color="negative" icon="cross" /> + <Button label="Edit" color="secondary" icon="Edit" /> + <Button label="Delete" color="negative" icon="Cross" /> </div> ), }, @@ -84,8 +84,8 @@ const rows = [ status: <Badge text="Inactive" color="negative" />, action: ( <div className="flex flex-row gap-3"> - <Button label="Edit" color="secondary" icon="edit" /> - <Button label="Delete" color="negative" icon="cross" /> + <Button label="Edit" color="secondary" icon="Edit" /> + <Button label="Delete" color="negative" icon="Cross" /> </div> ), }, @@ -96,8 +96,8 @@ const rows = [ status: <Badge text="Vacation" color="neutral" />, action: ( <div className="flex flex-row gap-3"> - <Button label="Edit" color="secondary" icon="edit" /> - <Button label="View" color="primary" icon="eye" /> + <Button label="Edit" color="secondary" icon="Edit" /> + <Button label="View" color="primary" icon="Eye" /> </div> ), }, @@ -110,14 +110,21 @@ export const TableWithText: StoryObj = { }, }; +export const EmptyTable: StoryObj = { + args: { + columns: textColumns, + rows: [], + }, +}; + export const TableWithComponentsInHeader: StoryObj = { args: { columns: textColumns, rows: textRows, headerContent: ( <div className="flex flex-row gap-2 pb-[10px]"> - <Button label="Create New" color="positive" icon="plus" /> - <Button label="Edit" color="secondary" icon="edit" /> + <Button label="Create New" color="positive" icon="Plus" /> + <Button label="Edit" color="secondary" icon="Edit" /> </div> ), }, @@ -175,8 +182,6 @@ export const TableWithReorderableRows = () => { ); }; - - type Row = { row_id: string; name: string; @@ -184,8 +189,7 @@ type Row = { salary: number; notes: string; action: JSX.Element; -} - +}; const editableColumnRows = [ { @@ -208,24 +212,38 @@ const editableColumnRows = [ role: "Project manager", salary: 2000000, notes: "Micro manager", - } + }, ] as Row[]; /** Cells can be edited, cell values can be validated */ export const TableWithEditableCells = () => { - const textEditor = (options: ColumnEditorOptions) => { - return <TextInput value={options.value as string} onValueChange={(e) => options.editorCallback && options.editorCallback(e)}></TextInput> + return ( + <TextInput + value={options.value as string} + onValueChange={(e) => + options.editorCallback && options.editorCallback(e) + } + ></TextInput> + ); }; const numberEditor = (options: ColumnEditorOptions) => { - return <TextInput type="number" value={options.value as string} onValueChange={(e) => options.editorCallback && options.editorCallback(e)}></TextInput> + return ( + <TextInput + type="number" + value={options.value as string} + onValueChange={(e) => + options.editorCallback && options.editorCallback(e) + } + ></TextInput> + ); }; const onCellEditComplete = (e: ColumnEvent) => { const { rowData, newValue, field } = e; rowData[field] = newValue; - } + }; const isPositiveInteger = (val: unknown) => { let str = String(val); @@ -233,14 +251,14 @@ export const TableWithEditableCells = () => { str = str.trim(); if (!str) { - return false; + return false; } - str = str.replace(/^0+/, '') || '0'; + str = str.replace(/^0+/, "") || "0"; const n = Math.floor(Number(str)); return n !== Infinity && String(n) === str && n >= 0; -}; + }; const onSalaryEditComplete = (e: ColumnEvent) => { const { rowData, newValue, field, originalEvent: event } = e; @@ -249,47 +267,81 @@ export const TableWithEditableCells = () => { } else { event.preventDefault(); } - } + }; const editableColumns = [ - { field: "row_id", header: "ID", style: { width: "30%" }}, - { field: "name", header: "Name", style: { width: "20%" }, editor: textEditor, onCellEditComplete: onCellEditComplete }, - { field: "role", header: "Role", style: { width: "20%" }}, - { field: "salary", header: "Salary", style: { width: "10%" }, editor: numberEditor, onCellEditComplete: onSalaryEditComplete}, - { field: "notes", header: "Notes", style: { width: "10%" }}, - { field: "action", header: "", style: { width: "10%" }}, + { field: "row_id", header: "ID", style: { width: "30%" } }, + { + field: "name", + header: "Name", + style: { width: "20%" }, + editor: textEditor, + onCellEditComplete: onCellEditComplete, + }, + { field: "role", header: "Role", style: { width: "20%" } }, + { + field: "salary", + header: "Salary", + style: { width: "10%" }, + editor: numberEditor, + onCellEditComplete: onSalaryEditComplete, + }, + { field: "notes", header: "Notes", style: { width: "10%" } }, + { field: "action", header: "", style: { width: "10%" } }, ]; const [editableRows, setEditableRows] = useState<Row[]>([]); - useEffect(() => { - setEditableRows(editableColumnRows) + useEffect(() => { + setEditableRows(editableColumnRows); }, []); - const deleteButton = (row: Row) => <Button label={"Delete"} icon={"cross"} onClick={() => { deleteEmployee(row) }} /> + const deleteButton = (row: Row) => ( + <Button + label="Delete" + icon="Cross" + onClick={() => { + deleteEmployee(row); + }} + /> + ); for (const row of editableRows) { row.action = deleteButton(row); } const addEmployee = () => { - const newRow = { row_id: crypto.randomUUID(), name: "", role: "", salary: 0, notes: "" } as Row; + const newRow = { + row_id: crypto.randomUUID(), + name: "", + role: "", + salary: 0, + notes: "", + } as Row; newRow.action = deleteButton(newRow); - setEditableRows(old => [...old, newRow]) + setEditableRows((old) => [...old, newRow]); }; const deleteEmployee = (row: Row) => { - const newRows = editableRows.filter(r => r !== row); + const newRows = editableRows.filter((r) => r !== row); setEditableRows(newRows); }; return ( - <Table - headerContent={<div className="flex flex-row gap-2 pb-[10px]"><Button color="positive" icon="plus" label="Add Employee" onClick={addEmployee}/></div>} - columns={editableColumns} - rows={editableRows} - editMode="cell" - emptyMessage="No employees found" - /> + <Table + headerContent={ + <div className="flex flex-row gap-2 pb-[10px]"> + <Button + color="positive" + icon="Plus" + label="Add Employee" + onClick={addEmployee} + /> + </div> + } + columns={editableColumns} + rows={editableRows} + editMode="cell" + /> ); -}; \ No newline at end of file +};