Skip to content

Commit

Permalink
Merge pull request #634 from sebgroup/develop
Browse files Browse the repository at this point in the history
release beta 16
  • Loading branch information
kherP authored Jul 16, 2021
2 parents fabbfa2 + a603042 commit 3a8bc83
Show file tree
Hide file tree
Showing 12 changed files with 1,107 additions and 1 deletion.
103 changes: 103 additions & 0 deletions docs/src/pages/docs/sortable-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import React from "react";
import Docs from "@common/Docs";
import { SortableList } from "@sebgroup/react-components/SortableList";
import { useDynamicForm } from "@sebgroup/react-components/hooks/useDynamicForm";
import { SortableItem } from "@sebgroup/react-components/SortableList/SortableItem";
import { Checkbox } from "@sebgroup/react-components/Checkbox";

const importString: string = require("!raw-loader!@sebgroup/react-components/SortableList/SortableList");
const code: string = `<SortableList>
<SortableItem uniqueKey="item1">item 1</SortableItem>
<SortableItem uniqueKey="item2">item 2</SortableItem>
<SortableItem uniqueKey="item3" disabled>item 3</SortableItem>
</SortableList>`;

type Example = {
label: string;
value: string;
checked: boolean;
disabled?: boolean;
};

const SortableListPage: React.FC = (): React.ReactElement<void> => {
const [value, setValue] = React.useState<number>(null);
const [array, setArray] = React.useState<Example[]>([
{
label: "Name",
value: "1",
checked: false,
},
{
label: "Age",
value: "2",
checked: false,
},
{
label: "Company",
value: "3",
checked: false,
},
{
label: "Address",
value: "4",
checked: false,
},
]);

const [renderControls, { controls }] = useDynamicForm([
{
key: "controls",
items: [
{ key: "disabled", label: "disabled", controlType: "Checkbox" },
{ key: "disabledItem", label: "disable one random item", controlType: "Checkbox" },
{ key: "simple", label: "simple usage", controlType: "Checkbox" },
],
},
]);

React.useEffect(() => {
setValue(controls.disabledItem ? Math.floor(Math.random() * (array.length - 1 - 0 + 1)) + 0 : null);
}, [controls.disabledItem]);

return (
<Docs
mainFile={importString}
example={
<div className="w-100 d-flex justify-content-center">
<SortableList
disabled={controls.disabled}
onSort={(list: string[]) => setArray((oldArray: Example[]) => oldArray.sort((a: Example, b: Example) => list.indexOf(a.value) - list.indexOf(b.value)))}
>
{array.map((item: Example, index: number) => (
<SortableItem key={index} uniqueKey={item.value} disabled={index === value}>
{controls.simple ? (
item.label
) : (
<Checkbox
name="test"
value={item.value}
checked={item.checked}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
setArray((oldArray: Example[]) =>
oldArray.map((checkbox: Example) => ({
...checkbox,
checked: item.value === checkbox.value ? event.target.checked : checkbox.checked,
}))
);
}}
>
{item.label}
</Checkbox>
)}
</SortableItem>
))}
</SortableList>
</div>
}
code={code}
controls={<>{renderControls()}</>}
/>
);
};

export default SortableListPage;
5 changes: 5 additions & 0 deletions docs/static/components-list.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@
"path": "/docs/stepper/",
"filePath": "./src/Stepper/index.ts"
},
{
"name": "SortableList",
"path": "/docs/sortable-list/",
"filePath": "./src/SortableList/index.ts"
},
{
"name": "Table",
"path": "/docs/table/",
Expand Down
2 changes: 1 addition & 1 deletion lib/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module.exports = {
testPathIgnorePatterns: ["node_modules", "\\.cache", "<rootDir>.*/public"],
transformIgnorePatterns: ["node_modules/(?!(@sebgroup|react|raf)/)"],
collectCoverage: true,
coveragePathIgnorePatterns: ["node_modules", "index.ts", "^.+\\.mock"],
coveragePathIgnorePatterns: ["node_modules", "index.ts", "^.+\\.mock", "^.+\\.polyfills"],
testEnvironmentOptions: { resources: "usable" },
globals: {
__PATH_PREFIX__: "",
Expand Down
50 changes: 50 additions & 0 deletions lib/src/SortableList/SortableItem.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from "react";
import { SortableItem, SortableItemProps } from ".";
import { unmountComponentAtNode, render } from "react-dom";
import { act } from "react-dom/test-utils";

describe("Component: SortableItem", () => {
let container: HTMLDivElement = null;
const props: SortableItemProps = { uniqueKey: "1" };

beforeEach(() => {
container = document.createElement("div");
document.body.appendChild(container);
});

afterEach(() => {
unmountComponentAtNode(container);
container.remove();
container = null;
});

it("Should render", () => {
act(() => {
render(<SortableItem {...props} />, container);
});
expect(container.querySelector(".sortable-item")).not.toBeNull();
});

it("Should pass a custom class and id", () => {
const className: string = "mySortableItemClass";
const id: string = "mySortableItemId";
act(() => {
render(<SortableItem {...props} className={className} id={id} />, container);
});
expect(container.querySelector(`.${className}`)).not.toBeNull();
expect(container.querySelector(`#${id}`)).not.toBeNull();
});

it("Should set children to disabled if disabled prop is passed", () => {
act(() => {
render(
<SortableItem {...props} disabled>
<input />
test
</SortableItem>,
container
);
});
expect(container.querySelector(`input`).hasAttribute("disabled")).toBeTruthy();
});
});
28 changes: 28 additions & 0 deletions lib/src/SortableList/SortableItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from "react";
import classnames from "classnames";

export type SortableItemProps = Omit<JSX.IntrinsicElements["div"], "onDragStart" | "onDragOver" | "onDragEnd"> & {
uniqueKey: string;
disabled?: boolean;
};

const SortableItem: React.FC<SortableItemProps> = React.forwardRef(
({ className, disabled, children, uniqueKey, ...props }: React.PropsWithChildren<SortableItemProps>, ref: React.ForwardedRef<HTMLDivElement>) => {
return (
<div {...props} ref={ref} className={classnames("rc", "sortable-item", className)}>
{React.Children.map(children, (Child: React.ReactElement) => {
return React.isValidElement<React.FC<any>>(Child)
? React.cloneElement(Child, {
disabled,
"aria-disabled": disabled,
} as any)
: Child;
})}
</div>
);
}
);

SortableItem.displayName = "SortableItem";

export { SortableItem };
116 changes: 116 additions & 0 deletions lib/src/SortableList/SortableList.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import React from "react";
import { SortableList, SortableItem, SortableItemProps, SortableListProps } from ".";
import { unmountComponentAtNode, render } from "react-dom";
import { act, Simulate } from "react-dom/test-utils";

describe("Component: SortableList", () => {
let container: HTMLDivElement = null;
const props: SortableListProps = { onSort: jest.fn() };

beforeEach(() => {
container = document.createElement("div");
document.body.appendChild(container);
});

afterEach(() => {
unmountComponentAtNode(container);
container.remove();
container = null;
});

it("Should render", () => {
act(() => {
render(
<SortableList {...props}>
<SortableItem uniqueKey="1" />
</SortableList>,
container
);
});
expect(container.querySelector(".sortable-list")).not.toBeNull();
});

it("Should pass a custom class and id", () => {
const className: string = "mySortableListClass";
const id: string = "mySortableListId";
act(() => {
render(
<SortableList {...props} className={className} id={id}>
<SortableItem uniqueKey="1" />
</SortableList>,
container
);
});
expect(container.querySelector(`.${className}`)).not.toBeNull();
expect(container.querySelector(`#${id}`)).not.toBeNull();
});

it("Should set children to disabled if disabled prop is passed", () => {
act(() => {
render(
<SortableList {...props} disabled>
<SortableItem uniqueKey="1">
<input />
test
</SortableItem>
</SortableList>,
container
);
});
expect(container.querySelector(`.disabled`)).not.toBeNull();
expect(container.querySelector(`input`).hasAttribute("disabled")).toBeTruthy();
});

it("Should allow to sort children by drag and drop", () => {
const children: { key: string; label: string }[] = [
{ key: "1", label: "1" },
{ key: "2", label: "2" },
{ key: "3", label: "3" },
];
act(() => {
render(
<SortableList {...props}>
{children.map((item) => (
<SortableItem key={item.key} uniqueKey={item.key}>
{item.label}
</SortableItem>
))}
</SortableList>,
container
);
});
const node: HTMLElement = container.querySelector(".sortable-item-wrapper");
(node.getBoundingClientRect as any) = jest.fn(() => {
return { top: 50, bottom: 500, right: 400, left: 200, height: 100, width: 50 };
});
act(() => {
Simulate.mouseDown(container.querySelector(".drag-icon"), { target: node, pageX: 50, pageY: 50 });
});
act(() => {
Simulate.dragStart(container.querySelector(".drag-icon"), { dataTransfer: { setDragImage: jest.fn() } } as any);
});
act(() => {
Simulate.dragOver(container.querySelectorAll(".sortable-item-wrapper")[1], { dataTransfer: {}, clientY: 50, target: node } as any);
});
act(() => {
Simulate.transitionEnd(container.querySelectorAll(".sortable-item-wrapper")[1]);
});
act(() => {
Simulate.dragEnd(container.querySelector(".drag-icon"));
});
expect(props.onSort).toBeCalledWith(["2", "1", "3"]);
});

it("Should throw error if no sortable item passed", () => {
const spyConsole: jest.SpyInstance = jest.spyOn(console, "warn");
act(() => {
render(
<SortableList {...props} disabled>
test
</SortableList>,
container
);
});
expect(spyConsole).toBeCalled();
});
});
Loading

0 comments on commit 3a8bc83

Please sign in to comment.