-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
patch(responsive): new release (v1.1.0)
- Loading branch information
1 parent
20b3976
commit 5573e37
Showing
4 changed files
with
219 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default as ISResponsive } from "./responsive"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |