Skip to content

Commit

Permalink
patch(responsive): new release (v1.1.0)
Browse files Browse the repository at this point in the history
  • Loading branch information
100percentibrahim committed Apr 20, 2024
1 parent 20b3976 commit 5573e37
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 0 deletions.
48 changes: 48 additions & 0 deletions packages/ui/responsive/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<div align="center">
<img width="20%" src="https://raw.githubusercontent.com/space-ibrahimstudio/ibrahimstudio/master/public/image/iss-logo.png" alt="@ibrahimstudio/responsive" />
<div align="center">
<h1>@ibrahimstudio/responsive</h1>
<h3>by: Ibrahim Space Studio</h3>
<p>This package provides automatic responsiveness for React applications.</p>
</div>
</div>

---

## 1. Installation

You can install this package via npm:

```sh
npm i @ibrahimstudio/responsive
# or
yarn add @ibrahimstudio/responsive
```

## 2. Usage

To use `@ibrahimstudio/responsive`, simply wrap your entire React application with the `<ISResponsive>` component:

```javascript
import React from "react";
import ReactDOM from "react-dom/client";
import { ISResponsive } from "@ibrahimstudio/responsive";
import App from "./App";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<ISResponsive>
<App />
</ISResponsive>
);
```

This will enable automatic responsiveness for your entire application by detecting elements with `px` in every elements. It calculates and adjusts these values based on the device type, ensuring optimal display across different screen sizes.

## 3. Contributing

Contributions are welcome! If you have any improvements, bug fixes, or features, feel free to open an issue or create a pull request on GitHub.

## 4. License

This project is licensed under the MIT License - see the [LICENSE](https://github.com/space-ibrahimstudio/ibrahimstudio/blob/master/LICENSE) file for details.
108 changes: 108 additions & 0 deletions packages/ui/responsive/__tests__/responsive.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import React from "react";
import { render } from "@testing-library/react";
import { useDeviceType, usePixelConverter } from "@ibrahimstudio/hooks";
import ISResponsive from "../src/responsive";
import "@testing-library/jest-dom";

jest.mock("react", () => ({
...jest.requireActual("react"),
useState: jest.fn(),
useEffect: jest.fn(),
}));

describe("useDeviceType", () => {
afterEach(() => {
jest.clearAllMocks();
});

it('should return "desktop" when window width is greater than 1120px', () => {
(window as any).innerWidth = 1200;
(React.useState as jest.Mock).mockReturnValueOnce(["desktop", jest.fn()]);

const deviceType = useDeviceType();
expect(deviceType).toBe("desktop");
});

it('should return "tablet" when window width is less than or equal to 1120px', () => {
(window as any).innerWidth = 800;
(React.useState as jest.Mock).mockReturnValueOnce(["tablet", jest.fn()]);

const deviceType = useDeviceType();
expect(deviceType).toBe("tablet");
});

it('should return "mobile" when window width is less than or equal to 700px', () => {
(window as any).innerWidth = 500;
(React.useState as jest.Mock).mockReturnValueOnce(["mobile", jest.fn()]);

const deviceType = useDeviceType();
expect(deviceType).toBe("mobile");
});
});

describe("usePixelConverter", () => {
afterEach(() => {
jest.clearAllMocks();
});

it("should convert pixel value based on device type", () => {
expect(usePixelConverter("100px", "mobile")).toBe("80px");
expect(usePixelConverter("100px", "tablet")).toBe("90px");
expect(usePixelConverter("100px", "desktop")).toBe("100px");
});

it("should return the original value if not in pixel format", () => {
expect(usePixelConverter("100%", "mobile")).toBe("100%");
});
});

describe("ISResponsive", () => {
afterEach(() => {
jest.clearAllMocks();
});

it("should render children with desktop device", () => {
(window as any).innerWidth = 1200;
(React.useState as jest.Mock).mockReturnValueOnce(["desktop", jest.fn()]);

const { getByTestId } = render(
<ISResponsive>
<div data-testid="test" style={{ width: "100px", height: "100px" }} />
</ISResponsive>
);

const testElement = getByTestId("test");
expect(testElement).toHaveStyle("width: 100px");
expect(testElement).toHaveStyle("height: 100px");
});

it("should render children with tablet device", () => {
(window as any).innerWidth = 800;
(React.useState as jest.Mock).mockReturnValueOnce(["tablet", jest.fn()]);

const { getByTestId } = render(
<ISResponsive>
<div data-testid="test" style={{ width: "100px", height: "100px" }} />
</ISResponsive>
);

const testElement = getByTestId("test");
expect(testElement).toHaveStyle("width: 90px");
expect(testElement).toHaveStyle("height: 90px");
});

it("should render children with mobile device", () => {
(window as any).innerWidth = 500;
(React.useState as jest.Mock).mockReturnValueOnce(["mobile", jest.fn()]);

const { getByTestId } = render(
<ISResponsive>
<div data-testid="test" style={{ width: "100px", height: "100px" }} />
</ISResponsive>
);

const testElement = getByTestId("test");
expect(testElement).toHaveStyle("width: 80px");
expect(testElement).toHaveStyle("height: 80px");
});
});
1 change: 1 addition & 0 deletions packages/ui/responsive/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as ISResponsive } from "./responsive";
62 changes: 62 additions & 0 deletions packages/ui/responsive/src/responsive.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from "react";
import { useDeviceType, usePixelConverter } from "@ibrahimstudio/hooks";

type DeviceType = "mobile" | "tablet" | "desktop";

const ISResponsive: React.FC<React.PropsWithChildren<{}>> = ({ children }) => {
const [deviceType, setDeviceType] = React.useState<DeviceType>("desktop");

React.useEffect(() => {
const handleResize = () => {
const newDeviceType = useDeviceType();
if (newDeviceType !== deviceType) {
setDeviceType(newDeviceType);
}
};

window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, [deviceType]);

const processStyles = (styles: React.CSSProperties): React.CSSProperties => {
const processedStyles: React.CSSProperties = {};
for (const key in styles) {
if (styles.hasOwnProperty(key)) {
const propKey = key as keyof React.CSSProperties;
const styleValue = styles[propKey];
if (typeof styleValue === "string" && styleValue.includes("px")) {
(processedStyles as any)[propKey] = usePixelConverter(
styleValue,
deviceType
);
} else {
(processedStyles as any)[propKey] = styleValue;
}
}
}
return processedStyles;
};

const executeChildren = (children: React.ReactNode): React.ReactNode => {
return React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
const updatedProps = {
...child.props,
style: processStyles(child.props.style || {}),
};
return React.cloneElement(
child,
updatedProps,
executeChildren(child.props.children)
);
}
return child;
});
};

return <>{executeChildren(children)}</>;
};

export default ISResponsive;

0 comments on commit 5573e37

Please sign in to comment.