Skip to content

Commit

Permalink
Added WithStack component
Browse files Browse the repository at this point in the history
  • Loading branch information
viktor-podzigun committed Dec 9, 2024
1 parent c92161c commit 9dc48e8
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 0 deletions.
59 changes: 59 additions & 0 deletions src/stack/WithStack.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* @typedef {import("@farjs/blessed").Widgets.BlessedElement} BlessedElement
*/
import React, { useContext } from "react";
import WithSize from "@farjs/ui/WithSize.mjs";
import PanelStack from "./PanelStack.mjs";

const h = React.createElement;

/**
* @typedef {{
* readonly isRight: boolean;
* readonly panelInput: BlessedElement;
* readonly stack: PanelStack;
* readonly width: number;
* readonly height: number;
* }} WithStackProps
*/

/**
* @param {WithStackProps} props
*/
const WithStack = (props) => {
const topComp = props.stack.peek().component;

return h(WithStack.withSizeComp, {
render: (width, height) => {
return h(
WithStack.Context.Provider,
{
value: { ...props, width, height },
},
h(topComp)
);
},
});
};

WithStack.displayName = "WithStack";
WithStack.withSizeComp = WithSize;

WithStack.Context = React.createContext(
/** @type {WithStackProps | null} */ (null)
);
/**
* @type {() => WithStackProps}
*/
WithStack.useStack = () => {
const ctx = useContext(WithStack.Context);
if (!ctx) {
throw Error(
"WithStack.Context is not found." +
"\nPlease, make sure you use WithStack.Context.Provider in parent component."
);
}
return ctx;
};

export default WithStack;
1 change: 1 addition & 0 deletions test/all.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ await import("./sort/FileListSort.test.mjs");

await import("./stack/PanelStack.test.mjs");
await import("./stack/PanelStackItem.test.mjs");
await import("./stack/WithStack.test.mjs");

await import("./theme/FileListTheme.test.mjs");
113 changes: 113 additions & 0 deletions test/stack/WithStack.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/**
* @typedef {import("@farjs/blessed").Widgets.BlessedElement} BlessedElement
* @typedef {import("../../src/stack/WithStack.mjs").WithStackProps} WithStackProps
*/
import React, { useContext } from "react";
import assert from "node:assert/strict";
import mockFunction from "mock-fn";
import TestRenderer from "react-test-renderer";
import {
assertComponents,
mockComponent,
TestErrorBoundary,
} from "react-assert";
import WithSize from "@farjs/ui/WithSize.mjs";
import PanelStackItem from "../../src/stack/PanelStackItem.mjs";
import PanelStack from "../../src/stack/PanelStack.mjs";
import WithStack from "../../src/stack/WithStack.mjs";

const h = React.createElement;

const { describe, it } = await (async () => {
// @ts-ignore
const module = process.isBun ? "bun:test" : "node:test";
// @ts-ignore
return process.isBun // @ts-ignore
? Promise.resolve({ describe: (_, fn) => fn(), it: test })
: import(module);
})();

const OtherComponent = () => {
return null;
};

WithStack.withSizeComp = mockComponent(WithSize);
const { withSizeComp } = WithStack;

describe("WithStack.test.mjs", () => {
it("should fail if no context when useStack", () => {
//given
// suppress intended error
// see: https://github.com/facebook/react/issues/11098#issuecomment-412682721
const savedConsoleError = console.error;
const consoleErrorMock = mockFunction();
console.error = consoleErrorMock;

const Wrapper = () => {
WithStack.useStack();
return h(React.Fragment);
};

//when
const result = TestRenderer.create(
h(TestErrorBoundary, null, h(Wrapper))
).root;

//then
console.error = savedConsoleError;
assert.deepEqual(consoleErrorMock.times, 1);
assertComponents(
result.children,
h(
"div",
null,
"Error: WithStack.Context is not found." +
"\nPlease, make sure you use WithStack.Context.Provider in parent component."
)
);
});

it("should render top component", () => {
//given
const [stackCtx, stackComp] = getStackCtxHook();
const top = new PanelStackItem(stackComp);
const other = new PanelStackItem(OtherComponent);
const stack = new PanelStack(false, [top, other], mockFunction());
const props = {
isRight: true,
panelInput: /** @type {BlessedElement} */ ({}),
stack,
width: 0,
height: 0,
};
const width = 25;
const height = 15;

//when
const comp = TestRenderer.create(h(WithStack, props)).root;
const withSizeProps = comp.findByType(withSizeComp).props;
const result = TestRenderer.create(
withSizeProps.render(width, height)
).root;

//then
assert.deepEqual(WithStack.displayName, "WithStack");
assert.deepEqual(stackCtx.current, { ...props, width, height });
assert.deepEqual(result.type, stackComp);
});
});

/**
* @returns {[React.MutableRefObject<WithStackProps | null>, () => React.ReactElement]}
*/
function getStackCtxHook() {
/** @type {React.MutableRefObject<WithStackProps | null>} */
const ref = React.createRef();
const comp = () => {
const ctx = useContext(WithStack.Context);
ref.current = ctx;
return h(React.Fragment, null);
};

return [ref, comp];
}
31 changes: 31 additions & 0 deletions types/stack/WithStack.d.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
export default WithStack;
export type BlessedElement = import("@farjs/blessed").Widgets.BlessedElement;
export type WithStackProps = {
readonly isRight: boolean;
readonly panelInput: BlessedElement;
readonly stack: PanelStack;
readonly width: number;
readonly height: number;
};
/**
* @typedef {{
* readonly isRight: boolean;
* readonly panelInput: BlessedElement;
* readonly stack: PanelStack;
* readonly width: number;
* readonly height: number;
* }} WithStackProps
*/
/**
* @param {WithStackProps} props
*/
declare function WithStack(props: WithStackProps): React.FunctionComponentElement<import("@farjs/ui/WithSize.mjs").WithSizeProps>;
declare namespace WithStack {
export const displayName: string;
export { WithSize as withSizeComp };
export const Context: React.Context<WithStackProps | null>;
export function useStack(): WithStackProps;
}
import PanelStack from "./PanelStack.mjs";
import React from "react";
import WithSize from "@farjs/ui/WithSize.mjs";

0 comments on commit 9dc48e8

Please sign in to comment.