-
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.
- Loading branch information
1 parent
c39fedc
commit 03eb345
Showing
4 changed files
with
279 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,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; |
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
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,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(), | ||
}) | ||
); | ||
} |
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,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"; |