mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Sort pod names by convering sub-parts (#3314)
This commit is contained in:
parent
e3e7c620a1
commit
64175b24e0
44
src/common/utils/array.ts
Normal file
44
src/common/utils/array.ts
Normal file
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
export type Tuple<T, N extends number> = N extends N ? number extends N ? T[] : _TupleOf<T, N, []> : never;
|
||||
type _TupleOf<T, N extends number, R extends unknown[]> = R["length"] extends N ? R : _TupleOf<T, N, [T, ...R]>;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param sources The source arrays
|
||||
* @yields A tuple of the next element from each of the sources
|
||||
* @returns The tuple of all the sources as soon as at least one of the sources is exausted
|
||||
*/
|
||||
export function* zipStrict<T, N extends number>(...sources: Tuple<T[], N>): Iterator<Tuple<T, N>, Tuple<T[], N>> {
|
||||
const maxSafeLength = sources.reduce((prev, cur) => Math.min(prev, cur.length), Number.POSITIVE_INFINITY);
|
||||
|
||||
if (!isFinite(maxSafeLength)) {
|
||||
// There are no sources, thus just return
|
||||
return [] as Tuple<T[], N>;
|
||||
}
|
||||
|
||||
for (let i = 0; i < maxSafeLength; i += 1) {
|
||||
yield sources.map(source => source[i]) as Tuple<T, N>;
|
||||
}
|
||||
|
||||
return sources.map(source => source.slice(maxSafeLength)) as Tuple<T[], N>;
|
||||
}
|
||||
@ -60,5 +60,6 @@ export * from "./convertMemory";
|
||||
export * from "./convertCpu";
|
||||
|
||||
import * as iter from "./iter";
|
||||
import * as array from "./array";
|
||||
|
||||
export { iter };
|
||||
export { iter, array };
|
||||
|
||||
@ -185,3 +185,19 @@ export function reduce<T, R = T>(src: Iterable<T>, reducer: (acc: R, cur: T) =>
|
||||
export function join(src: Iterable<string>, connector = ","): string {
|
||||
return reduce(src, (acc, cur) => `${acc}${connector}${cur}`, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate through `src` and return `true` if `fn` returns a thruthy value for every yielded value.
|
||||
* Otherwise, return `false`. This function short circuits.
|
||||
* @param src The type to be iterated over
|
||||
* @param fn A function to check each iteration
|
||||
*/
|
||||
export function every<T>(src: Iterable<T>, fn: (val: T) => any): boolean {
|
||||
for (const val of src) {
|
||||
if (!fn(val)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -24,16 +24,44 @@ import * as iter from "./iter";
|
||||
import type { RawHelmChart } from "../k8s-api/endpoints/helm-charts.api";
|
||||
import logger from "../logger";
|
||||
|
||||
export function sortCompare<T>(left: T, right: T): -1 | 0 | 1 {
|
||||
export enum Ordering {
|
||||
LESS = -1,
|
||||
EQUAL = 0,
|
||||
GREATER = 1,
|
||||
}
|
||||
|
||||
/**
|
||||
* This function switches the direction of `ordering` if `direction` is `"desc"`
|
||||
* @param ordering The original ordering (assumed to be an "asc" ordering)
|
||||
* @param direction The new desired direction
|
||||
*/
|
||||
export function rectifyOrdering(ordering: Ordering, direction: "asc" | "desc"): Ordering {
|
||||
if (direction === "desc") {
|
||||
return -ordering;
|
||||
}
|
||||
|
||||
return ordering;
|
||||
}
|
||||
|
||||
/**
|
||||
* An ascending sorting function
|
||||
* @param left An item from an array
|
||||
* @param right An item from an array
|
||||
* @returns The relative ordering in an ascending manner.
|
||||
* - Less if left < right
|
||||
* - Equal if left == right
|
||||
* - Greater if left > right
|
||||
*/
|
||||
export function sortCompare<T>(left: T, right: T): Ordering {
|
||||
if (left < right) {
|
||||
return -1;
|
||||
return Ordering.LESS;
|
||||
}
|
||||
|
||||
if (left === right) {
|
||||
return 0;
|
||||
return Ordering.EQUAL;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return Ordering.GREATER;
|
||||
}
|
||||
|
||||
interface ChartVersion {
|
||||
@ -41,17 +69,17 @@ interface ChartVersion {
|
||||
__version?: SemVer;
|
||||
}
|
||||
|
||||
export function sortCompareChartVersions(left: ChartVersion, right: ChartVersion): -1 | 0 | 1 {
|
||||
export function sortCompareChartVersions(left: ChartVersion, right: ChartVersion): Ordering {
|
||||
if (left.__version && right.__version) {
|
||||
return semver.compare(right.__version, left.__version);
|
||||
}
|
||||
|
||||
if (!left.__version && right.__version) {
|
||||
return 1;
|
||||
return Ordering.GREATER;
|
||||
}
|
||||
|
||||
if (left.__version && !right.__version) {
|
||||
return -1;
|
||||
return Ordering.LESS;
|
||||
}
|
||||
|
||||
return sortCompare(left.version, right.version);
|
||||
|
||||
@ -71,11 +71,11 @@ export class DeploymentReplicaSets extends React.Component<Props> {
|
||||
<DrawerTitle title="Deploy Revisions"/>
|
||||
<Table
|
||||
selectable
|
||||
tableId="deployment_replica_sets_view"
|
||||
scrollable={false}
|
||||
sortable={this.sortingCallbacks}
|
||||
sortByDefault={{ sortBy: sortBy.pods, orderBy: "desc" }}
|
||||
sortSyncWithUrl={false}
|
||||
tableId="deployment_replica_sets_view"
|
||||
className="box grow"
|
||||
>
|
||||
<TableHead>
|
||||
|
||||
@ -31,7 +31,7 @@ import { eventStore } from "../+events/event.store";
|
||||
import { KubeObjectListLayout } from "../kube-object-list-layout";
|
||||
import { nodesApi, Pod } from "../../../common/k8s-api/endpoints";
|
||||
import { StatusBrick } from "../status-brick";
|
||||
import { cssNames, stopPropagation } from "../../utils";
|
||||
import { cssNames, getConvertedParts, stopPropagation } from "../../utils";
|
||||
import toPairs from "lodash/toPairs";
|
||||
import startCase from "lodash/startCase";
|
||||
import kebabCase from "lodash/kebabCase";
|
||||
@ -98,7 +98,7 @@ export class Pods extends React.Component<Props> {
|
||||
tableId = "workloads_pods"
|
||||
isConfigurable
|
||||
sortingCallbacks={{
|
||||
[columnId.name]: pod => pod.getName(),
|
||||
[columnId.name]: pod => getConvertedParts(pod.getName()),
|
||||
[columnId.namespace]: pod => pod.getNs(),
|
||||
[columnId.containers]: pod => pod.getContainers().length,
|
||||
[columnId.restarts]: pod => pod.getRestartsCount(),
|
||||
|
||||
155
src/renderer/components/table/__tests__/getSorted.test.ts
Normal file
155
src/renderer/components/table/__tests__/getSorted.test.ts
Normal file
@ -0,0 +1,155 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { cloneDeep } from "lodash";
|
||||
import { getSorted } from "../sorting";
|
||||
|
||||
describe("Table tests", () => {
|
||||
describe("getSorted", () => {
|
||||
it.each([undefined, 5, "", true, {}, []])("should not sort since %j is not a function", () => {
|
||||
expect(getSorted([1, 2, 4, 3], undefined, "asc")).toStrictEqual([1, 2, 4, 3]);
|
||||
});
|
||||
|
||||
it("should sort numerically asc and not touch the original list", () => {
|
||||
const i = [1, 2, 4, 3];
|
||||
|
||||
expect(getSorted(i, v => v, "asc")).toStrictEqual([1, 2, 3, 4]);
|
||||
expect(i).toStrictEqual([1, 2, 4, 3]);
|
||||
});
|
||||
|
||||
it("should sort numerically desc and not touch the original list", () => {
|
||||
const i = [1, 2, 4, 3];
|
||||
|
||||
expect(getSorted(i, v => v, "desc")).toStrictEqual([4, 3, 2, 1]);
|
||||
expect(i).toStrictEqual([1, 2, 4, 3]);
|
||||
});
|
||||
|
||||
it("should sort numerically asc (by defaul) and not touch the original list", () => {
|
||||
const i = [1, 2, 4, 3];
|
||||
|
||||
expect(getSorted(i, v => v, "foobar")).toStrictEqual([1, 2, 3, 4]);
|
||||
expect(i).toStrictEqual([1, 2, 4, 3]);
|
||||
});
|
||||
|
||||
describe("multi-part", () => {
|
||||
it("should sort each part by its order", () => {
|
||||
const i = ["a", "c", "b.1", "b.2", "d"];
|
||||
|
||||
expect(getSorted(i, v => v.split("."), "desc")).toStrictEqual(["d", "c", "b.2", "b.1", "a"]);
|
||||
expect(i).toStrictEqual(["a", "c", "b.1", "b.2", "d"]);
|
||||
});
|
||||
|
||||
it("should be a stable sort", () => {
|
||||
const i = [{
|
||||
val: "a",
|
||||
k: 1,
|
||||
}, {
|
||||
val: "c",
|
||||
k: 2
|
||||
}, {
|
||||
val: "b.1",
|
||||
k: 3
|
||||
}, {
|
||||
val: "b.2",
|
||||
k: 4
|
||||
}, {
|
||||
val: "d",
|
||||
k: 5
|
||||
}, {
|
||||
val: "b.2",
|
||||
k: -10
|
||||
}];
|
||||
const dup = cloneDeep(i);
|
||||
const expected = [
|
||||
{
|
||||
val: "a",
|
||||
k: 1,
|
||||
}, {
|
||||
val: "b.1",
|
||||
k: 3
|
||||
}, {
|
||||
val: "b.2",
|
||||
k: 4
|
||||
}, {
|
||||
val: "b.2",
|
||||
k: -10
|
||||
}, {
|
||||
val: "c",
|
||||
k: 2
|
||||
}, {
|
||||
val: "d",
|
||||
k: 5
|
||||
},
|
||||
];
|
||||
|
||||
expect(getSorted(i, ({ val }) => val.split("."), "asc")).toStrictEqual(expected);
|
||||
expect(i).toStrictEqual(dup);
|
||||
});
|
||||
|
||||
it("should be a stable sort #2", () => {
|
||||
const i = [{
|
||||
val: "a",
|
||||
k: 1,
|
||||
}, {
|
||||
val: "b.2",
|
||||
k: -10
|
||||
}, {
|
||||
val: "c",
|
||||
k: 2
|
||||
}, {
|
||||
val: "b.1",
|
||||
k: 3
|
||||
}, {
|
||||
val: "b.2",
|
||||
k: 4
|
||||
}, {
|
||||
val: "d",
|
||||
k: 5
|
||||
}];
|
||||
const dup = cloneDeep(i);
|
||||
const expected = [
|
||||
{
|
||||
val: "a",
|
||||
k: 1,
|
||||
}, {
|
||||
val: "b.1",
|
||||
k: 3
|
||||
}, {
|
||||
val: "b.2",
|
||||
k: -10
|
||||
}, {
|
||||
val: "b.2",
|
||||
k: 4
|
||||
}, {
|
||||
val: "c",
|
||||
k: 2
|
||||
}, {
|
||||
val: "d",
|
||||
k: 5
|
||||
},
|
||||
];
|
||||
|
||||
expect(getSorted(i, ({ val }) => val.split("."), "asc")).toStrictEqual(expected);
|
||||
expect(i).toStrictEqual(dup);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
68
src/renderer/components/table/sorting.ts
Normal file
68
src/renderer/components/table/sorting.ts
Normal file
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import type { TableSortCallback } from "./table";
|
||||
import { array, Ordering, rectifyOrdering, sortCompare } from "../../utils";
|
||||
|
||||
export function getSorted<T>(rawItems: T[], sortingCallback: TableSortCallback<T> | undefined, orderByRaw: string): T[] {
|
||||
if (typeof sortingCallback !== "function") {
|
||||
return rawItems;
|
||||
}
|
||||
|
||||
const orderBy = orderByRaw === "asc" || orderByRaw === "desc" ? orderByRaw : "asc";
|
||||
const sortData = rawItems.map((item, index) => ({
|
||||
index,
|
||||
sortBy: sortingCallback(item),
|
||||
}));
|
||||
|
||||
sortData.sort((left, right) => {
|
||||
if (!Array.isArray(left.sortBy) && !Array.isArray(right.sortBy)) {
|
||||
return rectifyOrdering(sortCompare(left.sortBy, right.sortBy), orderBy);
|
||||
}
|
||||
|
||||
const leftSortBy = [left.sortBy].flat();
|
||||
const rightSortBy = [right.sortBy].flat();
|
||||
const zipIter = array.zipStrict(leftSortBy, rightSortBy);
|
||||
let r = zipIter.next();
|
||||
|
||||
for (; r.done === false; r = zipIter.next()) {
|
||||
const [nextL, nextR] = r.value;
|
||||
|
||||
const sortOrder = rectifyOrdering(sortCompare(nextL, nextR), orderBy);
|
||||
|
||||
if (sortOrder !== Ordering.EQUAL) {
|
||||
return sortOrder;
|
||||
}
|
||||
}
|
||||
|
||||
const [leftRest, rightRest] = r.value;
|
||||
|
||||
return leftRest.length - rightRest.length;
|
||||
});
|
||||
|
||||
const res = [];
|
||||
|
||||
for (const { index } of sortData) {
|
||||
res.push(rawItems[index]);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
@ -22,9 +22,8 @@
|
||||
import "./table.scss";
|
||||
|
||||
import React from "react";
|
||||
import { orderBy } from "lodash";
|
||||
import { observer } from "mobx-react";
|
||||
import { boundMethod, cssNames, noop } from "../../utils";
|
||||
import { boundMethod, cssNames } from "../../utils";
|
||||
import { TableRow, TableRowElem, TableRowProps } from "./table-row";
|
||||
import { TableHead, TableHeadElem, TableHeadProps } from "./table-head";
|
||||
import type { TableCellElem } from "./table-cell";
|
||||
@ -32,6 +31,7 @@ import { VirtualList } from "../virtual-list";
|
||||
import { createPageParam } from "../../navigation";
|
||||
import { getSortParams, setSortParams } from "./table.storage";
|
||||
import { computed, makeObservable } from "mobx";
|
||||
import { getSorted } from "./sorting";
|
||||
|
||||
export type TableSortBy = string;
|
||||
export type TableOrderBy = "asc" | "desc" | string;
|
||||
@ -92,16 +92,22 @@ export class Table<Item> extends React.Component<TableProps<Item>> {
|
||||
const { sortable, tableId } = this.props;
|
||||
|
||||
if (sortable && !tableId) {
|
||||
console.error("[Table]: sorted table requires props.tableId to be specified");
|
||||
console.error("Table must have props.tableId if props.sortable is specified");
|
||||
}
|
||||
}
|
||||
|
||||
@computed get isSortable() {
|
||||
const { sortable, tableId } = this.props;
|
||||
|
||||
return Boolean(sortable && tableId);
|
||||
}
|
||||
|
||||
@computed get sortParams() {
|
||||
return Object.assign({}, this.props.sortByDefault, getSortParams(this.props.tableId));
|
||||
}
|
||||
|
||||
renderHead() {
|
||||
const { sortable, children } = this.props;
|
||||
const { children } = this.props;
|
||||
const content = React.Children.toArray(children) as (TableRowElem | TableHeadElem)[];
|
||||
const headElem: React.ReactElement<TableHeadProps> = content.find(elem => elem.type === TableHead);
|
||||
|
||||
@ -109,7 +115,7 @@ export class Table<Item> extends React.Component<TableProps<Item>> {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (sortable) {
|
||||
if (this.isSortable) {
|
||||
const columns = React.Children.toArray(headElem.props.children) as TableCellElem[];
|
||||
|
||||
return React.cloneElement(headElem, {
|
||||
@ -136,14 +142,12 @@ export class Table<Item> extends React.Component<TableProps<Item>> {
|
||||
return headElem;
|
||||
}
|
||||
|
||||
getSorted(items: any[]) {
|
||||
const { sortBy, orderBy: order } = this.sortParams;
|
||||
const sortingCallback = this.props.sortable[sortBy] || noop;
|
||||
getSorted(rawItems: Item[]) {
|
||||
const { sortBy, orderBy: orderByRaw } = this.sortParams;
|
||||
|
||||
return orderBy(items, sortingCallback, order as any);
|
||||
return getSorted(rawItems, this.props.sortable[sortBy], orderByRaw);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
protected onSort({ sortBy, orderBy }: TableSortParams) {
|
||||
setSortParams(this.props.tableId, { sortBy, orderBy });
|
||||
const { sortSyncWithUrl, onSort } = this.props;
|
||||
@ -153,9 +157,7 @@ export class Table<Item> extends React.Component<TableProps<Item>> {
|
||||
orderByUrlParam.set(orderBy);
|
||||
}
|
||||
|
||||
if (onSort) {
|
||||
onSort({ sortBy, orderBy });
|
||||
}
|
||||
onSort?.({ sortBy, orderBy });
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
@ -183,12 +185,12 @@ export class Table<Item> extends React.Component<TableProps<Item>> {
|
||||
}
|
||||
|
||||
renderRows() {
|
||||
const { sortable, noItems, virtual, customRowHeights, rowLineHeight, rowPadding, items, getTableRow, selectedItemId, className } = this.props;
|
||||
const { noItems, virtual, customRowHeights, rowLineHeight, rowPadding, items, getTableRow, selectedItemId, className } = this.props;
|
||||
const content = this.getContent();
|
||||
let rows: React.ReactElement<TableRowProps>[] = content.filter(elem => elem.type === TableRow);
|
||||
let sortedItems = rows.length ? rows.map(row => row.props.sortItem) : [...items];
|
||||
|
||||
if (sortable) {
|
||||
if (this.isSortable) {
|
||||
sortedItems = this.getSorted(sortedItems);
|
||||
|
||||
if (rows.length) {
|
||||
@ -228,15 +230,13 @@ export class Table<Item> extends React.Component<TableProps<Item>> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { selectable, scrollable, sortable, autoSize, virtual } = this.props;
|
||||
let { className } = this.props;
|
||||
|
||||
className = cssNames("Table flex column", className, {
|
||||
selectable, scrollable, sortable, autoSize, virtual,
|
||||
const { selectable, scrollable, autoSize, virtual, className } = this.props;
|
||||
const classNames = cssNames("Table flex column", className, {
|
||||
selectable, scrollable, sortable: this.isSortable, autoSize, virtual,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<div className={classNames}>
|
||||
{this.renderHead()}
|
||||
{this.renderRows()}
|
||||
</div>
|
||||
|
||||
36
src/renderer/utils/__tests__/converted-name.test.ts
Normal file
36
src/renderer/utils/__tests__/converted-name.test.ts
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { getConvertedParts } from "../name-parts";
|
||||
|
||||
describe("getConvertedParts", () => {
|
||||
it.each([
|
||||
["hello", ["hello"]],
|
||||
["hello.goodbye", ["hello", "goodbye"]],
|
||||
["hello.1", ["hello", 1]],
|
||||
["3-hello.1", [3, "hello", 1]],
|
||||
["3_hello.1", [3, "hello", 1]],
|
||||
["3_hello.1/foobar", [3, "hello", 1, "foobar"]],
|
||||
["3_hello.1/foobar\\new", [3, "hello", 1, "foobar", "new"]],
|
||||
])("Splits '%s' as into %j", (input, output) => {
|
||||
expect(getConvertedParts(input)).toEqual(output);
|
||||
});
|
||||
});
|
||||
@ -22,19 +22,18 @@
|
||||
// Common usage utils & helpers
|
||||
|
||||
export * from "../../common/utils";
|
||||
|
||||
export * from "./cssVar";
|
||||
export * from "./cssNames";
|
||||
export * from "../../common/event-emitter";
|
||||
export * from "./saveFile";
|
||||
export * from "./prevDefault";
|
||||
export * from "./storageHelper";
|
||||
export * from "./createStorage";
|
||||
export * from "./interval";
|
||||
|
||||
export * from "./copyToClipboard";
|
||||
export * from "./isReactNode";
|
||||
export * from "../../common/utils/convertMemory";
|
||||
export * from "../../common/utils/convertCpu";
|
||||
export * from "./metricUnitsToNumber";
|
||||
export * from "./createStorage";
|
||||
export * from "./cssNames";
|
||||
export * from "./cssVar";
|
||||
export * from "./display-booleans";
|
||||
export * from "./interval";
|
||||
export * from "./isMiddleClick";
|
||||
export * from "./isReactNode";
|
||||
export * from "./metricUnitsToNumber";
|
||||
export * from "./name-parts";
|
||||
export * from "./prevDefault";
|
||||
export * from "./saveFile";
|
||||
export * from "./storageHelper";
|
||||
|
||||
36
src/renderer/utils/name-parts.ts
Normal file
36
src/renderer/utils/name-parts.ts
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Split `name` into the parts seperated by one or more of (-, _, or .) and
|
||||
* the sections can be converted to numbers will be converted
|
||||
* @param name A kube object name
|
||||
* @returns The converted parts of the name
|
||||
*/
|
||||
export function getConvertedParts(name: string): (string | number)[] {
|
||||
return name
|
||||
.split(/[-_./\\]+/)
|
||||
.map(part => {
|
||||
const converted = +part;
|
||||
|
||||
return isNaN(converted) ? part : converted;
|
||||
});
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user