mirror of
https://github.com/home-assistant/frontend.git
synced 2026-10-10 17:18:43 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d8da6d263 | ||
|
|
5154ba5d8e | ||
|
|
84e4a5f55c |
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
createQueryString,
|
||||
decodeQueryParams,
|
||||
type QueryParamConfig,
|
||||
type QueryParamValues,
|
||||
type SearchParamsSource,
|
||||
} from "./query-params";
|
||||
|
||||
const marketplaceBrowseQueryParamConfig = {
|
||||
list: ["status", "type", "source"],
|
||||
string: ["search", "sort", "direction"],
|
||||
} as const satisfies QueryParamConfig;
|
||||
|
||||
export type MarketplaceBrowseQueryParams = QueryParamValues<
|
||||
typeof marketplaceBrowseQueryParamConfig
|
||||
>;
|
||||
|
||||
export const decodeMarketplaceBrowseQueryParams = (
|
||||
searchParams: SearchParamsSource
|
||||
): MarketplaceBrowseQueryParams =>
|
||||
decodeQueryParams(searchParams, marketplaceBrowseQueryParamConfig);
|
||||
|
||||
export const createMarketplaceBrowseQueryString = (
|
||||
values: MarketplaceBrowseQueryParams
|
||||
): string => createQueryString(values, marketplaceBrowseQueryParamConfig);
|
||||
@@ -1,4 +1,8 @@
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import {
|
||||
createMarketplaceBrowseQueryString,
|
||||
decodeMarketplaceBrowseQueryParams,
|
||||
} from "../../../common/url/marketplace-query-params";
|
||||
import type { DataTableRowData } from "../../../components/data-table/ha-data-table";
|
||||
import type { RepositoryBase } from "../../../data/marketplace/repository";
|
||||
import type { MarketplaceTab } from "./ha-marketplace-dashboard";
|
||||
@@ -22,8 +26,6 @@ export const STATUS_FILTER = "status";
|
||||
export const TYPE_FILTER = "type";
|
||||
|
||||
export const SOURCE_FILTER = "source";
|
||||
const SORT_PARAM = "sort";
|
||||
const DIRECTION_PARAM = "direction";
|
||||
|
||||
// Where a repository comes from, the community catalog or added from a link
|
||||
export const SOURCE_ORDER = ["catalog", "custom"] as const;
|
||||
@@ -150,55 +152,54 @@ export const filterRepositories = (
|
||||
}));
|
||||
|
||||
// How to browse, the way a link says it, like
|
||||
// /marketplace/browse?sort=stars&direction=desc&status=new
|
||||
// /marketplace/browse?status=new&search=card&sort=stars&direction=desc
|
||||
export interface BrowseSettings {
|
||||
search?: string;
|
||||
sorting?: { column: string; direction: "asc" | "desc" };
|
||||
filters: RepositoryFilters;
|
||||
}
|
||||
|
||||
export const browseUrl = ({ sorting, filters }: BrowseSettings): string => {
|
||||
const params = new URLSearchParams();
|
||||
if (sorting) {
|
||||
params.set(SORT_PARAM, sorting.column);
|
||||
params.set(DIRECTION_PARAM, sorting.direction);
|
||||
}
|
||||
|
||||
for (const filter of [STATUS_FILTER, TYPE_FILTER, SOURCE_FILTER] as const) {
|
||||
if (filters[filter]?.length) {
|
||||
params.set(filter, filters[filter].join(","));
|
||||
}
|
||||
}
|
||||
|
||||
const query = params.toString();
|
||||
export const browseUrl = ({
|
||||
search,
|
||||
sorting,
|
||||
filters,
|
||||
}: BrowseSettings): string => {
|
||||
const query = createMarketplaceBrowseQueryString({
|
||||
...filters,
|
||||
search,
|
||||
sort: sorting?.column,
|
||||
direction: sorting?.direction,
|
||||
});
|
||||
return query ? `/marketplace/browse?${query}` : "/marketplace/browse";
|
||||
};
|
||||
|
||||
// Nothing about browsing in the link keeps what was picked before
|
||||
export const browseSettingsFromUrl = (
|
||||
search: string
|
||||
query: string
|
||||
): BrowseSettings | undefined => {
|
||||
const params = new URLSearchParams(search);
|
||||
const { search, sort, direction, ...lists } =
|
||||
decodeMarketplaceBrowseQueryParams(query);
|
||||
|
||||
const filters: RepositoryFilters = {
|
||||
[STATUS_FILTER]: lists.status?.filter(Boolean),
|
||||
[TYPE_FILTER]: lists.type?.filter(Boolean),
|
||||
[SOURCE_FILTER]: lists.source?.filter(Boolean),
|
||||
};
|
||||
|
||||
if (
|
||||
![SORT_PARAM, STATUS_FILTER, TYPE_FILTER, SOURCE_FILTER].some((key) =>
|
||||
params.has(key)
|
||||
)
|
||||
!search &&
|
||||
!sort &&
|
||||
!Object.values(filters).some((values) => values?.length)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const column = params.get(SORT_PARAM);
|
||||
const values = (key: string) => params.get(key)?.split(",").filter(Boolean);
|
||||
return {
|
||||
sorting: column
|
||||
? {
|
||||
column,
|
||||
direction: params.get(DIRECTION_PARAM) === "asc" ? "asc" : "desc",
|
||||
}
|
||||
search,
|
||||
sorting: sort
|
||||
? // Ascending unless the link says otherwise, like picking a sort in the table
|
||||
{ column: sort, direction: direction === "desc" ? "desc" : "asc" }
|
||||
: undefined,
|
||||
filters: {
|
||||
[STATUS_FILTER]: values(STATUS_FILTER),
|
||||
[TYPE_FILTER]: values(TYPE_FILTER),
|
||||
[SOURCE_FILTER]: values(SOURCE_FILTER),
|
||||
},
|
||||
filters,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -263,7 +263,7 @@ export class HaMarketplaceDashboard extends LitElement {
|
||||
updateHistoryState({ [APPLIED_LINK_STATE]: search });
|
||||
this._linksApplied++;
|
||||
// A search left behind would hide part of what the link promised
|
||||
this._activeSearch = "";
|
||||
this._activeSearch = settings.search ?? "";
|
||||
this._filters = settings.filters;
|
||||
if (settings.sorting) {
|
||||
this._activeSorting = settings.sorting;
|
||||
|
||||
@@ -21,6 +21,16 @@ import { documentationUrl } from "../../util/documentation-url";
|
||||
// That's why redirects should be sorted with more specific ones first
|
||||
// Or else pressing "M" will link to the higher level page.
|
||||
|
||||
// The Marketplace tabs with a table read these from the link
|
||||
const marketplaceTableParams = {
|
||||
search: "string?",
|
||||
type: "string?",
|
||||
status: "string?",
|
||||
source: "string?",
|
||||
sort: "string?",
|
||||
direction: "string?",
|
||||
} satisfies Redirect["params"];
|
||||
|
||||
export const getMyRedirects = (): Redirects => ({
|
||||
application_credentials: {
|
||||
redirect: "/config/application_credentials",
|
||||
@@ -469,6 +479,24 @@ export const getMyRedirects = (): Redirects => ({
|
||||
category: "string?",
|
||||
},
|
||||
},
|
||||
marketplace_discover: {
|
||||
component: "marketplace",
|
||||
redirect: "/marketplace/discover",
|
||||
},
|
||||
marketplace_browse: {
|
||||
component: "marketplace",
|
||||
redirect: "/marketplace/browse",
|
||||
params: marketplaceTableParams,
|
||||
},
|
||||
marketplace_installed: {
|
||||
component: "marketplace",
|
||||
redirect: "/marketplace/installed",
|
||||
params: marketplaceTableParams,
|
||||
},
|
||||
marketplace_repositories: {
|
||||
component: "marketplace",
|
||||
redirect: "/marketplace/repositories",
|
||||
},
|
||||
});
|
||||
|
||||
const getRedirect = (path: string): Redirect | undefined =>
|
||||
|
||||
@@ -161,6 +161,7 @@ describe("filterRepositories", () => {
|
||||
describe("browseUrl", () => {
|
||||
it("links to what browseSettingsFromUrl reads back", () => {
|
||||
const settings = {
|
||||
search: "mushroom card",
|
||||
sorting: { column: "stars", direction: "desc" as const },
|
||||
filters: { status: ["new"], type: ["theme"], source: ["custom"] },
|
||||
};
|
||||
@@ -170,6 +171,19 @@ describe("browseUrl", () => {
|
||||
expect(url.pathname).toBe("/marketplace/browse");
|
||||
expect(browseSettingsFromUrl(url.search)).toEqual(settings);
|
||||
});
|
||||
|
||||
it("sorts ascending when the link gives no direction", () => {
|
||||
expect(browseSettingsFromUrl("?sort=name")?.sorting).toEqual({
|
||||
column: "name",
|
||||
direction: "asc",
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps what was picked for a link that says nothing about browsing", () => {
|
||||
expect(
|
||||
browseSettingsFromUrl("?owner=hacs&repository=integration")
|
||||
).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("filtersOfTab", () => {
|
||||
|
||||
Reference in New Issue
Block a user