Skip to content

Commit

Permalink
Added SortModesPopup component
Browse files Browse the repository at this point in the history
  • Loading branch information
viktor-podzigun committed Dec 13, 2024
1 parent c39fedc commit 03eb345
Show file tree
Hide file tree
Showing 4 changed files with 279 additions and 0 deletions.
78 changes: 78 additions & 0 deletions src/sort/SortModesPopup.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* @typedef {import("./SortMode.mjs").SortMode} SortMode
* @typedef {import("./FileListSort.mjs").FileListSort} FileListSort
*/
import React from "react";
import MenuPopup from "@farjs/ui/menu/MenuPopup.mjs";
import WithStack from "../stack/WithStack.mjs";
import SortMode from "./SortMode.mjs";

const h = React.createElement;

/**
* @typedef {{
* readonly sort: FileListSort;
* onClose(): void;
* }} SortModesPopupProps
*/

/**
* @param {SortModesPopupProps} props
*/
const SortModesPopup = (props) => {
const { menuPopup } = SortModesPopup;

const stackProps = WithStack.useStack();
const showOnLeft = !stackProps.isRight;

const items = itemsAndModes.map(({ item, mode }) => {
const indicator = props.sort.asc ? "+" : "-";
return mode === props.sort.mode ? `${indicator}${item.substring(1)}` : item;
});

/** @type {(index: number) => void} */
function onSelect(index) {
props.onClose();

const key = index + 3;
stackProps.panelInput.emit("keypress", undefined, {
name: `f${key}+ctrl`,
full: `C-f${key}`,
ctrl: true,
});
}

return h(menuPopup, {
title: "Sort by",
items,
getLeft: (width) => {
return MenuPopup.getLeftPos(stackProps.width, showOnLeft, width);
},
onSelect,
onClose: props.onClose,
});
};

SortModesPopup.displayName = "SortModesPopup";
SortModesPopup.menuPopup = MenuPopup;

/**
* @param {string} item
* @param {SortMode} mode
* @returns {{item: string, mode: SortMode}}
*/
function makeItem(item, mode) {
return { item, mode };
}

const itemsAndModes = [
makeItem(" Name Ctrl-F3 ", SortMode.Name),
makeItem(" Extension Ctrl-F4 ", SortMode.Extension),
makeItem(" Modification time Ctrl-F5 ", SortMode.ModificationTime),
makeItem(" Size Ctrl-F6 ", SortMode.Size),
makeItem(" Unsorted Ctrl-F7 ", SortMode.Unsorted),
makeItem(" Creation time Ctrl-F8 ", SortMode.CreationTime),
makeItem(" Access time Ctrl-F9 ", SortMode.AccessTime),
];

export default SortModesPopup;
1 change: 1 addition & 0 deletions test/all.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ await import("./history/HistoryProvider.test.mjs");

await import("./sort/FileListSort.test.mjs");
await import("./sort/SortIndicator.test.mjs");
await import("./sort/SortModesPopup.test.mjs");

await import("./stack/PanelStack.test.mjs");
await import("./stack/PanelStackItem.test.mjs");
Expand Down
171 changes: 171 additions & 0 deletions test/sort/SortModesPopup.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/**
* @typedef {import("../../src/sort/FileListSort.mjs").FileListSort} FileListSort
* @typedef {import("../../src/sort/SortModesPopup.mjs").SortModesPopupProps} SortModesPopupProps
*/
import React from "react";
import TestRenderer from "react-test-renderer";
import assert from "node:assert/strict";
import { assertComponents, mockComponent } from "react-assert";
import mockFunction from "mock-fn";
import MenuPopup from "@farjs/ui/menu/MenuPopup.mjs";
import PanelStack from "../../src/stack/PanelStack.mjs";
import withStackContext from "../../src/stack/withStackContext.mjs";
import SortMode from "../../src/sort/SortMode.mjs";
import FileListSort from "../../src/sort/FileListSort.mjs";
import SortModesPopup from "../../src/sort/SortModesPopup.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);
})();

SortModesPopup.menuPopup = mockComponent(MenuPopup);

const { menuPopup } = SortModesPopup;

describe("SortModesPopup.test.mjs", () => {
it("should emit keypress event and call onClose when onSelect", () => {
//given
const stack = new PanelStack(true, [], mockFunction());
const onClose = mockFunction();
let emitArgs = /** @type {any[]} */ ([]);
const emitMock = mockFunction((...args) => {
emitArgs = args;
});
const inputMock = { emit: emitMock };
const props = getSortModesPopupProps(
FileListSort(SortMode.Name, true),
onClose
);
const comp = TestRenderer.create(
withStackContext(h(SortModesPopup, props), {
stack,
width: 40,
panelInput: /** @type {any} */ (inputMock),
})
).root;
const menuProps = comp.findByType(menuPopup).props;

//when
menuProps.onSelect(0);

//then
assert.deepEqual(onClose.times, 1);
assert.deepEqual(emitArgs, [
"keypress",
undefined,
{
name: "f3+ctrl",
full: "C-f3",
ctrl: true,
},
]);
});

it("should call onClose when onClose", () => {
//given
const stack = new PanelStack(true, [], mockFunction());
const onClose = mockFunction();
const props = getSortModesPopupProps(
FileListSort(SortMode.Name, true),
onClose
);
const comp = TestRenderer.create(
withStackContext(h(SortModesPopup, props), { stack, width: 40 })
).root;
const menuProps = comp.findByType(menuPopup).props;

//when
menuProps.onClose();

//then
assert.deepEqual(onClose.times, 1);
});

it("should render popup on left panel", () => {
//given
const isRight = false;
const stack = new PanelStack(true, [], mockFunction());
const width = 40;
const props = getSortModesPopupProps(
FileListSort(SortMode.Extension, true)
);

//when
const result = TestRenderer.create(
withStackContext(h(SortModesPopup, props), { isRight, stack, width })
).root;

//then
assertSortModesPopup(result, props, isRight, width);
});

it("should render popup on right panel", () => {
//given
const isRight = true;
const stack = new PanelStack(true, [], mockFunction());
const width = 40;
const props = getSortModesPopupProps(
FileListSort(SortMode.Extension, false)
);

//when
const result = TestRenderer.create(
withStackContext(h(SortModesPopup, props), { isRight, stack, width })
).root;

//then
assertSortModesPopup(result, props, isRight, width);
});
});

/**
* @param {FileListSort} sort
* @param {() => void} onClose
* @returns {SortModesPopupProps}
*/
function getSortModesPopupProps(sort, onClose = mockFunction()) {
return { sort, onClose };
}

/**
* @param {TestRenderer.ReactTestInstance} result
* @param {SortModesPopupProps} props
* @param {boolean} isRight
* @param {number} stackWidth
*/
function assertSortModesPopup(result, props, isRight, stackWidth) {
assert.deepEqual(SortModesPopup.displayName, "SortModesPopup");

const menuProps = result.findByType(menuPopup).props;
assert.deepEqual(
menuProps.getLeft(50),
MenuPopup.getLeftPos(stackWidth, !isRight, 50)
);
const indicator = props.sort.asc ? "+" : "-";

assertComponents(
result.children,
h(menuPopup, {
title: "Sort by",
items: [
" Name Ctrl-F3 ",
`${indicator} Extension Ctrl-F4 `,
" Modification time Ctrl-F5 ",
" Size Ctrl-F6 ",
" Unsorted Ctrl-F7 ",
" Creation time Ctrl-F8 ",
" Access time Ctrl-F9 ",
],
getLeft: menuProps.getLeft,
onSelect: mockFunction(),
onClose: mockFunction(),
})
);
}
29 changes: 29 additions & 0 deletions types/sort/SortModesPopup.d.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export default SortModesPopup;
export type SortMode = import("./SortMode.mjs").SortMode;
export type FileListSort = import("./FileListSort.mjs").FileListSort;
export type SortModesPopupProps = {
readonly sort: FileListSort;
onClose(): void;
};
/**
* @typedef {{
* readonly sort: FileListSort;
* onClose(): void;
* }} SortModesPopupProps
*/
/**
* @param {SortModesPopupProps} props
*/
declare function SortModesPopup(props: SortModesPopupProps): React.FunctionComponentElement<{
title: string;
items: string[];
getLeft: (width: number) => string;
onSelect: (index: number) => void;
onClose: () => void;
}>;
declare namespace SortModesPopup {
export const displayName: string;
export { MenuPopup as menuPopup };
}
import React from "react";
import MenuPopup from "@farjs/ui/menu/MenuPopup.mjs";

0 comments on commit 03eb345

Please sign in to comment.