Skip to content

Commit 232ac3d

Browse files
committed
Merge branch 'master' of https://github.com/jsonwebtoken/jsonwebtoken.github.io into gh-actions-testing-stale-pr
2 parents 3532fd8 + 1d8ca39 commit 232ac3d

File tree

12 files changed

+225
-42
lines changed

12 files changed

+225
-42
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,6 @@
6565
"prettier": "^3.2.5",
6666
"typescript": "^5.4.5",
6767
"vite-tsconfig-paths": "^4.3.2",
68-
"vitest": "^1.4.0"
68+
"vitest": "^1.6.1"
6969
}
7070
}

src/app/[language]/libraries/page.tsx

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { LibraryCategoryModel } from "@/features/libraries/models/library-catego
99
import { DATA_PATH } from "@/libs/config/project-paths.constants";
1010
import { DEFAULT_LANGUAGE_CODE } from "@/features/localization/localization.config";
1111
import { getLibrariesDictionary } from "@/features/localization/services/language-dictionary.service";
12-
import { LIBRARIES_FILTER_DEFAULT_VALUE } from "@/libs/config/project.constants";
1312
import { StructuredData } from "@/features/seo/components/structured-data.component";
1413
import { generateArticleStructuredData } from "@/features/seo/services/structured-data.service";
1514
import { PageMetadataProps } from "@/features/common/models/page-metadata.props";
@@ -18,11 +17,13 @@ import { generatePageMetadata } from "@/libs/metadata/metadata.service";
1817
import { createUrlPath } from "@/libs/utils/path.utils";
1918
import { siteTree } from "@/features/seo/site-tree";
2019
import { getAuth0Dictionary } from "@/features/localization/services/ui-language-dictionary.service";
20+
import { LibraryModel } from "@/features/libraries/models/library.model";
21+
import { LibrariesDictionaryModel } from "@/features/localization/models/libraries-dictionary.model";
2122

2223
export async function generateMetadata({
2324
params: { language },
2425
}: PageMetadataProps): Promise<Metadata> {
25-
const dictionary = getLibrariesDictionary(language);
26+
const dictionary: LibrariesDictionaryModel = getLibrariesDictionary(language);
2627

2728
return generatePageMetadata({
2829
languageCode: language,
@@ -37,7 +38,9 @@ export default function Libraries({
3738
}: {
3839
params: { language: string };
3940
searchParams?: {
40-
filter?: string;
41+
programming_language?: string;
42+
algorithm?: keyof LibraryModel["support"];
43+
support?: keyof LibraryModel["support"];
4144
};
4245
}) {
4346
const librariesDictionary = getLibrariesDictionary(languageCode);
@@ -47,19 +50,55 @@ export default function Libraries({
4750
encoding: "utf-8",
4851
});
4952

50-
const query: string | null = searchParams?.filter || "";
53+
const programmingLanguage = searchParams?.programming_language;
54+
const algorithm = searchParams?.algorithm;
55+
const support = searchParams?.support;
56+
const query = programmingLanguage ?? algorithm ?? support ?? "";
5157
const dictionary = JSON.parse(source) as LibraryDictionaryModel;
58+
const allOptions = Object.keys(Object.values(dictionary)[0].libs[0].support);
59+
const indexAlgorithmStart = allOptions.findIndex(
60+
(option) => option === "hs256"
61+
);
5262

5363
const categoryOptions: { id: string; name: string }[] = Object.values(
54-
dictionary,
64+
dictionary
5565
).map((library) => ({
5666
id: library.id,
5767
name: library.name,
5868
}));
5969

60-
let categories: LibraryCategoryModel[] = dictionary[query]
61-
? [dictionary[query]]
62-
: Object.values(dictionary);
70+
const supportOptions: { value: string; label: string }[] = allOptions
71+
.slice(0, indexAlgorithmStart)
72+
.map((key) => ({
73+
value: key,
74+
label: key.toUpperCase(),
75+
}));
76+
77+
const algorithmOptions: { value: string; label: string }[] = allOptions
78+
.slice(indexAlgorithmStart)
79+
.map((key) => ({
80+
value: key,
81+
label: key.toUpperCase(),
82+
}));
83+
84+
const categories: LibraryCategoryModel[] =
85+
programmingLanguage && programmingLanguage !== "all"
86+
? [dictionary[programmingLanguage]]
87+
: Object.values(dictionary);
88+
89+
const categoryToFilter = algorithm ?? support;
90+
91+
const filteredCategories = categoryToFilter
92+
? categories.map((category) => {
93+
const filteredLibs = category.libs.filter(
94+
(lib) => lib.support[categoryToFilter]
95+
);
96+
return {
97+
...category,
98+
libs: filteredLibs,
99+
};
100+
})
101+
: categories;
63102

64103
return (
65104
<>
@@ -166,11 +205,13 @@ export default function Libraries({
166205
languageCode={languageCode}
167206
query={query || librariesDictionary.filterPicker.defaultValue.value}
168207
categoryOptions={categoryOptions}
208+
algorithmOptions={algorithmOptions}
209+
supportOptions={supportOptions}
169210
dictionary={librariesDictionary}
170211
/>
171212
<LibraryResultsComponent
172213
languageCode={languageCode}
173-
categories={categories}
214+
categories={filteredCategories}
174215
dictionary={librariesDictionary}
175216
/>
176217
<Auth0CtaComponent

src/features/common/components/debugger-picker/debugger-picker.component.tsx

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,30 @@
11
import React, { useEffect, useState } from "react";
22
import styles from "./debugger-picker.module.scss";
3-
import Select, { SingleValue } from "react-select";
3+
import Select, { SingleValue, OptionsOrGroups, GroupBase } from "react-select";
44
import { DebuggerPickerOptionModel } from "@/features/common/models/debugger-picker-option.model";
5+
import { LibraryFilterLabel } from "@/features/libraries/models/library-filters.model";
6+
import { isGroupedOptionsType } from "./utils";
7+
58

69
interface PickerLabelProps {
710
label: string | null;
811
}
912

13+
const getGroupLabel = (
14+
options: OptionsOrGroups<
15+
DebuggerPickerOptionModel,
16+
GroupBase<DebuggerPickerOptionModel>
17+
>,
18+
selected: DebuggerPickerOptionModel
19+
): LibraryFilterLabel | undefined => {
20+
if(!isGroupedOptionsType(options)) return undefined
21+
22+
const group = (options as GroupBase<DebuggerPickerOptionModel>[]).find(
23+
(group) => group.options.some((opt) => opt.value === selected.value)
24+
);
25+
return group ? group.label as LibraryFilterLabel : undefined;
26+
};
27+
1028
const PickerLabel: React.FC<PickerLabelProps> = ({ label }) => {
1129
return (
1230
<div className={styles.picker__label}>
@@ -18,9 +36,16 @@ const PickerLabel: React.FC<PickerLabelProps> = ({ label }) => {
1836
interface DebuggerPickerComponentProps {
1937
label: string | null;
2038
languageCode: string;
21-
options: DebuggerPickerOptionModel[];
39+
options: OptionsOrGroups<
40+
DebuggerPickerOptionModel,
41+
GroupBase<DebuggerPickerOptionModel>
42+
>;
43+
isGrouped?: boolean;
2244
selectedOptionCode: DebuggerPickerOptionModel | null;
23-
handleSelection: (value: string) => void;
45+
handleSelection: (
46+
selection: string,
47+
parentLabel?: LibraryFilterLabel
48+
) => void;
2449
placeholder: string | null;
2550
minWidth: string | null;
2651
}
@@ -37,12 +62,14 @@ export const DebuggerPickerComponent: React.FC<
3762
}) => {
3863
const [isClient, setIsClient] = useState(false);
3964

40-
const handleChange = (selection: SingleValue<DebuggerPickerOptionModel>) => {
65+
const handleChange = (
66+
selection: SingleValue<DebuggerPickerOptionModel>
67+
) => {
4168
if (!selection) {
4269
return;
4370
}
44-
45-
handleSelection(selection.value);
71+
const groupLabel = getGroupLabel(options, selection);
72+
handleSelection(selection.value, groupLabel);
4673
};
4774

4875
useEffect(() => {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { GroupBase, OptionsOrGroups } from "react-select";
2+
import { DebuggerPickerOptionModel } from "../../models/debugger-picker-option.model";
3+
4+
export const isGroupedOptionsType = (
5+
options: OptionsOrGroups<
6+
DebuggerPickerOptionModel,
7+
GroupBase<DebuggerPickerOptionModel>
8+
>
9+
): options is GroupBase<DebuggerPickerOptionModel>[] => {
10+
return "options" in options[0]
11+
};
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import { LibraryFilterLabel } from "@/features/libraries/models/library-filters.model";
2+
13
export interface DebuggerPickerOptionModel {
24
value: any;
3-
label: string;
5+
label: string | LibraryFilterLabel;
46
isDisabled?: boolean;
57
}

src/features/common/services/utils.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,14 @@ export const extractJwt = (value: string): string => {
2222
return "";
2323
}
2424

25-
const jwt = value.split(/\s+/).filter((element) => element.startsWith("ey"));
25+
// Check if it's a JWT string with newlines - compact it if so
26+
if (value.trim().startsWith("ey") && (value.match(/\./g) || []).length >= 2) {
27+
// It looks like a valid JWT, so remove all whitespace (including newlines)
28+
return value.replace(/\s+/g, "");
29+
}
2630

31+
// Otherwise, use the extraction logic to find JWT in a larger text block
32+
const jwt = value.split(/\s+/).filter((element) => element.startsWith("ey"));
2733
return jwt[0] || value;
2834
};
2935

src/features/debugger/components/debugger-alg-picker/debugger-alg-picker.component.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ import { useDecoderStore } from "@/features/decoder/services/decoder.store";
1111
import { useDebuggerStore } from "@/features/debugger/services/debugger.store";
1212
import { DebuggerWidgetValues } from "@/features/common/values/debugger-widget.values";
1313
import { DebuggerPickerComponent } from "@/features/common/components/debugger-picker/debugger-picker.component";
14-
import { DebuggerPickerOptionModel } from "@/features/common/models/debugger-picker-option.model";
1514
import {
1615
algDictionary,
1716
jwsExampleAlgHeaderParameterValuesDictionary,
1817
} from "@/features/common/values/jws-alg-header-parameter-values.dictionary";
1918
import { useButton } from "@react-aria/button";
2019
import { clsx } from "clsx";
2120
import { PrimaryFont } from "@/libs/theme/fonts";
21+
import { DebuggerPickerOptionModel } from "@/features/common/models/debugger-picker-option.model";
2222

2323
enum PickerStates {
2424
IDLE = "IDLE",

src/features/libraries/components/library-hero/library-hero.component.tsx

Lines changed: 61 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,59 +3,96 @@
33
import React, { useMemo } from "react";
44
import styles from "./library-hero.module.scss";
55
import { BoxComponent } from "@/features/common/components/box/box.component";
6-
import { usePathname, useRouter, useSearchParams } from "next/navigation";
7-
import { LIBRARIES_FILTER_QUERY_PARAM_KEY } from "@/libs/config/project.constants";
6+
import { usePathname, useRouter } from "next/navigation";
7+
import {
8+
LIBRARIES_FILTER_ALGORITHM_KEY,
9+
LIBRARIES_FILTER_PROGRAMMING_LANGUAGE_KEY,
10+
LIBRARIES_FILTER_SUPPORT_KEY,
11+
} from "@/libs/config/project.constants";
812
import { clsx } from "clsx";
913
import { getLocalizedSecondaryFont } from "@/libs/theme/fonts";
1014
import { LibrariesDictionaryModel } from "@/features/localization/models/libraries-dictionary.model";
1115
import { DebuggerPickerComponent } from "@/features/common/components/debugger-picker/debugger-picker.component";
16+
import { LibraryFilterLabel } from "../../models/library-filters.model";
17+
import { DebuggerPickerOptionModel } from "@/features/common/models/debugger-picker-option.model";
18+
import { GroupBase } from "react-select";
1219

1320
interface LibraryHeroComponentProps {
1421
languageCode: string;
1522
query: string;
1623
categoryOptions: { id: string; name: string }[];
24+
algorithmOptions: { value: string; label: string }[];
25+
supportOptions: { value: string; label: string }[];
1726
dictionary: LibrariesDictionaryModel;
1827
}
1928

2029
export const LibraryHeroComponent: React.FC<LibraryHeroComponentProps> = ({
2130
languageCode,
2231
query,
2332
categoryOptions,
33+
algorithmOptions,
34+
supportOptions,
2435
dictionary,
2536
}) => {
26-
const searchParams = useSearchParams();
2737
const pathname = usePathname();
2838
const { replace } = useRouter();
2939

30-
const handleSelection = (selection: string) => {
31-
const params = new URLSearchParams(searchParams);
32-
33-
if (selection) {
34-
params.set(LIBRARIES_FILTER_QUERY_PARAM_KEY, selection);
35-
} else {
36-
params.delete(LIBRARIES_FILTER_QUERY_PARAM_KEY);
40+
const handleSelection = (
41+
selection: string,
42+
parentLabel?: LibraryFilterLabel
43+
) => {
44+
if (!parentLabel) {
45+
return;
46+
}
47+
const params = new URLSearchParams("");
48+
switch (parentLabel) {
49+
case "ProgrammingLanguage":
50+
params.set(LIBRARIES_FILTER_PROGRAMMING_LANGUAGE_KEY, selection);
51+
break;
52+
case "Algorithm":
53+
params.set(LIBRARIES_FILTER_ALGORITHM_KEY, selection);
54+
break;
55+
case "Support":
56+
params.set(LIBRARIES_FILTER_SUPPORT_KEY, selection);
57+
break;
58+
default:
59+
break;
3760
}
38-
3961
replace(`${pathname}?${params.toString()}`);
4062
};
4163

4264
const options = useMemo(() => {
4365
return [
4466
{
45-
value: dictionary.filterPicker.defaultValue.value,
46-
label: dictionary.filterPicker.defaultValue.label,
67+
label: "ProgrammingLanguage",
68+
options: [
69+
{
70+
value: dictionary.filterPicker.defaultValue.value,
71+
label: dictionary.filterPicker.defaultValue.label,
72+
},
73+
...categoryOptions.map((categoryOption) => {
74+
return {
75+
value: categoryOption.id,
76+
label: categoryOption.name,
77+
};
78+
}),
79+
],
80+
},
81+
{
82+
label: "Support",
83+
options: [...supportOptions],
84+
},
85+
{
86+
label: "Algorithm",
87+
options: [...algorithmOptions],
4788
},
48-
...categoryOptions.map((categoryOption) => {
49-
return {
50-
value: categoryOption.id,
51-
label: categoryOption.name,
52-
};
53-
}),
54-
];
89+
] as GroupBase<DebuggerPickerOptionModel>[];
5590
}, [
5691
categoryOptions,
5792
dictionary.filterPicker.defaultValue.label,
5893
dictionary.filterPicker.defaultValue.value,
94+
algorithmOptions,
95+
supportOptions
5996
]);
6097

6198
return (
@@ -67,7 +104,7 @@ export const LibraryHeroComponent: React.FC<LibraryHeroComponentProps> = ({
67104
<h1
68105
className={clsx(
69106
styles.heroTitle,
70-
getLocalizedSecondaryFont(languageCode),
107+
getLocalizedSecondaryFont(languageCode)
71108
)}
72109
>
73110
{dictionary.title}
@@ -80,7 +117,9 @@ export const LibraryHeroComponent: React.FC<LibraryHeroComponentProps> = ({
80117
languageCode={languageCode}
81118
options={options}
82119
selectedOptionCode={
83-
options.filter((option) => option.value === query)[0]
120+
options
121+
.flatMap((group) => group.options)
122+
.filter((option) => option.value === query)[0]
84123
}
85124
handleSelection={handleSelection}
86125
placeholder={null}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export type LibraryFilterLabel = "ProgrammingLanguage" | "Algorithm" | "Support"

0 commit comments

Comments
 (0)