mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Hotbar inner drag-n-drop (#2691)
* Configure ts to use react-jsx rule Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Moving HotbarSelector to separate component Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Initial drag-n-drop implementation Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Revert tsconfig and linter changes Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Reverting back active cell effect Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Adding drag-n-drop behavior Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Fix drag-n-drop logic Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * White border on dragging over Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Adding test coverage Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Fixing cell hover effect Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Increase PageLayout z-index Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Styling hotbar selector tooltip Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com>
This commit is contained in:
1 parent
83e63bf959
commit
1af12fe59e
9 files changed
+404
-96
No files matched your search
@@ -1,7 +1,71 @@
|
||||
import mockFs from "mock-fs";
|
||||
import { CatalogEntityItem } from "../../renderer/components/+catalog/catalog-entity.store";
|
||||
import { ClusterStore } from "../cluster-store";
|
||||
import { HotbarStore } from "../hotbar-store";
|
||||
|
||||
const testCluster = {
|
||||
uid: "test",
|
||||
name: "test",
|
||||
apiVersion: "v1",
|
||||
kind: "Cluster",
|
||||
status: {
|
||||
phase: "Running"
|
||||
},
|
||||
spec: {},
|
||||
getName: jest.fn(),
|
||||
getId: jest.fn(),
|
||||
onDetailsOpen: jest.fn(),
|
||||
onContextMenuOpen: jest.fn(),
|
||||
onSettingsOpen: jest.fn(),
|
||||
metadata: {
|
||||
uid: "test",
|
||||
name: "test",
|
||||
labels: {}
|
||||
}
|
||||
};
|
||||
|
||||
const minikubeCluster = {
|
||||
uid: "minikube",
|
||||
name: "minikube",
|
||||
apiVersion: "v1",
|
||||
kind: "Cluster",
|
||||
status: {
|
||||
phase: "Running"
|
||||
},
|
||||
spec: {},
|
||||
getName: jest.fn(),
|
||||
getId: jest.fn(),
|
||||
onDetailsOpen: jest.fn(),
|
||||
onContextMenuOpen: jest.fn(),
|
||||
onSettingsOpen: jest.fn(),
|
||||
metadata: {
|
||||
uid: "minikube",
|
||||
name: "minikube",
|
||||
labels: {}
|
||||
}
|
||||
};
|
||||
|
||||
const awsCluster = {
|
||||
uid: "aws",
|
||||
name: "aws",
|
||||
apiVersion: "v1",
|
||||
kind: "Cluster",
|
||||
status: {
|
||||
phase: "Running"
|
||||
},
|
||||
spec: {},
|
||||
getName: jest.fn(),
|
||||
getId: jest.fn(),
|
||||
onDetailsOpen: jest.fn(),
|
||||
onContextMenuOpen: jest.fn(),
|
||||
onSettingsOpen: jest.fn(),
|
||||
metadata: {
|
||||
uid: "aws",
|
||||
name: "aws",
|
||||
labels: {}
|
||||
}
|
||||
};
|
||||
|
||||
describe("HotbarStore", () => {
|
||||
beforeEach(() => {
|
||||
ClusterStore.resetInstance();
|
||||
@@ -31,4 +95,137 @@ describe("HotbarStore", () => {
|
||||
expect(hotbarStore.hotbars.length).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("hotbar items", () => {
|
||||
it("initially creates 12 empty cells", () => {
|
||||
const hotbarStore = HotbarStore.createInstance();
|
||||
|
||||
hotbarStore.load();
|
||||
expect(hotbarStore.getActive().items.length).toEqual(12);
|
||||
});
|
||||
|
||||
it("adds items", () => {
|
||||
const hotbarStore = HotbarStore.createInstance();
|
||||
const entity = new CatalogEntityItem(testCluster);
|
||||
|
||||
hotbarStore.load();
|
||||
hotbarStore.addToHotbar(entity);
|
||||
const items = hotbarStore.getActive().items.filter(Boolean);
|
||||
|
||||
expect(items.length).toEqual(1);
|
||||
});
|
||||
|
||||
it("removes items", () => {
|
||||
const hotbarStore = HotbarStore.createInstance();
|
||||
const entity = new CatalogEntityItem(testCluster);
|
||||
|
||||
hotbarStore.load();
|
||||
hotbarStore.addToHotbar(entity);
|
||||
hotbarStore.removeFromHotbar("test");
|
||||
const items = hotbarStore.getActive().items.filter(Boolean);
|
||||
|
||||
expect(items.length).toEqual(0);
|
||||
});
|
||||
|
||||
it("does nothing if removing with invalid uid", () => {
|
||||
const hotbarStore = HotbarStore.createInstance();
|
||||
const entity = new CatalogEntityItem(testCluster);
|
||||
|
||||
hotbarStore.load();
|
||||
hotbarStore.addToHotbar(entity);
|
||||
hotbarStore.removeFromHotbar("invalid uid");
|
||||
const items = hotbarStore.getActive().items.filter(Boolean);
|
||||
|
||||
expect(items.length).toEqual(1);
|
||||
});
|
||||
|
||||
it("moves item to empty cell", () => {
|
||||
const hotbarStore = HotbarStore.createInstance();
|
||||
const test = new CatalogEntityItem(testCluster);
|
||||
const minikube = new CatalogEntityItem(minikubeCluster);
|
||||
const aws = new CatalogEntityItem(awsCluster);
|
||||
|
||||
hotbarStore.load();
|
||||
hotbarStore.addToHotbar(test);
|
||||
hotbarStore.addToHotbar(minikube);
|
||||
hotbarStore.addToHotbar(aws);
|
||||
|
||||
expect(hotbarStore.getActive().items[5]).toBeNull();
|
||||
|
||||
hotbarStore.restackItems(1, 5);
|
||||
|
||||
expect(hotbarStore.getActive().items[5]).toBeTruthy();
|
||||
expect(hotbarStore.getActive().items[5].entity.uid).toEqual("minikube");
|
||||
});
|
||||
|
||||
it("moves items down", () => {
|
||||
const hotbarStore = HotbarStore.createInstance();
|
||||
const test = new CatalogEntityItem(testCluster);
|
||||
const minikube = new CatalogEntityItem(minikubeCluster);
|
||||
const aws = new CatalogEntityItem(awsCluster);
|
||||
|
||||
hotbarStore.load();
|
||||
hotbarStore.addToHotbar(test);
|
||||
hotbarStore.addToHotbar(minikube);
|
||||
hotbarStore.addToHotbar(aws);
|
||||
|
||||
// aws -> test
|
||||
hotbarStore.restackItems(2, 0);
|
||||
|
||||
const items = hotbarStore.getActive().items.map(item => item?.entity.uid || null);
|
||||
|
||||
expect(items.slice(0, 4)).toEqual(["aws", "test", "minikube", null]);
|
||||
});
|
||||
|
||||
it("moves items up", () => {
|
||||
const hotbarStore = HotbarStore.createInstance();
|
||||
const test = new CatalogEntityItem(testCluster);
|
||||
const minikube = new CatalogEntityItem(minikubeCluster);
|
||||
const aws = new CatalogEntityItem(awsCluster);
|
||||
|
||||
hotbarStore.load();
|
||||
hotbarStore.addToHotbar(test);
|
||||
hotbarStore.addToHotbar(minikube);
|
||||
hotbarStore.addToHotbar(aws);
|
||||
|
||||
// test -> aws
|
||||
hotbarStore.restackItems(0, 2);
|
||||
|
||||
const items = hotbarStore.getActive().items.map(item => item?.entity.uid || null);
|
||||
|
||||
expect(items.slice(0, 4)).toEqual(["minikube", "aws", "test", null]);
|
||||
});
|
||||
|
||||
it("does nothing when item moved to same cell", () => {
|
||||
const hotbarStore = HotbarStore.createInstance();
|
||||
const test = new CatalogEntityItem(testCluster);
|
||||
|
||||
hotbarStore.load();
|
||||
hotbarStore.addToHotbar(test);
|
||||
hotbarStore.restackItems(0, 0);
|
||||
|
||||
expect(hotbarStore.getActive().items[0].entity.uid).toEqual("test");
|
||||
});
|
||||
|
||||
it("throws if invalid arguments provided", () => {
|
||||
// Prevent writing to stderr during this render.
|
||||
const err = console.error;
|
||||
|
||||
console.error = jest.fn();
|
||||
|
||||
const hotbarStore = HotbarStore.createInstance();
|
||||
const test = new CatalogEntityItem(testCluster);
|
||||
|
||||
hotbarStore.load();
|
||||
hotbarStore.addToHotbar(test);
|
||||
|
||||
expect(() => hotbarStore.restackItems(-5, 0)).toThrow();
|
||||
expect(() => hotbarStore.restackItems(2, -1)).toThrow();
|
||||
expect(() => hotbarStore.restackItems(14, 1)).toThrow();
|
||||
expect(() => hotbarStore.restackItems(11, 112)).toThrow();
|
||||
|
||||
// Restore writing to stderr.
|
||||
console.error = err;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -4,7 +4,6 @@ import migrations from "../migrations/hotbar-store";
|
||||
import * as uuid from "uuid";
|
||||
import { CatalogEntityItem } from "../renderer/components/+catalog/catalog-entity.store";
|
||||
import isNull from "lodash/isNull";
|
||||
import { CatalogEntity } from "./catalog/catalog-entity";
|
||||
|
||||
export interface HotbarItem {
|
||||
entity: {
|
||||
@@ -147,9 +146,9 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
||||
}
|
||||
}
|
||||
|
||||
removeFromHotbar(item: CatalogEntity) {
|
||||
removeFromHotbar(uid: string) {
|
||||
const hotbar = this.getActive();
|
||||
const index = hotbar.items.findIndex((i) => i?.entity.uid === item.getId());
|
||||
const index = hotbar.items.findIndex((i) => i?.entity.uid === uid);
|
||||
|
||||
if (index == -1) {
|
||||
return;
|
||||
@@ -158,6 +157,40 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
||||
hotbar.items[index] = null;
|
||||
}
|
||||
|
||||
findClosestEmptyIndex(from: number, direction = 1) {
|
||||
let index = from;
|
||||
|
||||
while(this.getActive().items[index] != null) {
|
||||
index += direction;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
restackItems(from: number, to: number): void {
|
||||
const { items } = this.getActive();
|
||||
const source = items[from];
|
||||
const moveDown = from < to;
|
||||
|
||||
if (from < 0 || to < 0 || from >= items.length || to >= items.length || isNaN(from) || isNaN(to)) {
|
||||
throw new Error("Invalid 'from' or 'to' arguments");
|
||||
}
|
||||
|
||||
if (from == to) {
|
||||
return;
|
||||
}
|
||||
|
||||
items.splice(from, 1, null);
|
||||
|
||||
if (items[to] == null) {
|
||||
items.splice(to, 1, source);
|
||||
} else {
|
||||
// Move cells up or down to closes empty cell
|
||||
items.splice(this.findClosestEmptyIndex(to, moveDown ? -1 : 1), 1);
|
||||
items.splice(to, 0, source);
|
||||
}
|
||||
}
|
||||
|
||||
switchToPrevious() {
|
||||
const hotbarStore = HotbarStore.getInstance();
|
||||
let index = hotbarStore.activeHotbarIndex - 1;
|
||||
|
||||
Reference in new issue
Block a user