Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b29c78e86d | |||
| aac0f3b3a9 |
@@ -0,0 +1,15 @@
|
||||
# Discovery title order follows UI language
|
||||
|
||||
10 November 2026. Baseline `aac0f3b3a9b452b48126ddaf26e1837eeadd2f6d`, branch `codex/i18n-discovery-order`.
|
||||
|
||||
The title sort used the browser's default collation and its memo omitted the UI language. Switching English to Bokmål left Å-prefixed titles before Z. The focused regression reproduced this on the unchanged baseline.
|
||||
|
||||
`JobDiscoveryPage` now passes its existing English/Bokmål display locale to `localeCompare` and includes that locale in the memo dependencies. The same locale still formats recorded timestamps. The change reaches ordinary and saved-search result arrays through their shared title sort.
|
||||
|
||||
The regression switches English → Bokmål → English with Å/Z fixtures, verifies each order, and checks that the role, municipality and title sort remain selected. Switching adds zero API reads and triggers no writes. Existing submitted search and provider contracts are retained.
|
||||
|
||||
24 tests in `job-discovery.test.tsx`, TypeScript without emit and ESLint on the two changed files passed. The initial red run selected one test and skipped the other 23. It is retained separately from the final suite result in `evidence/discovery-title-order/`.
|
||||
|
||||
Export build, rendered browser, native language/device and live-provider verification were not run in this isolated increment. Existing whole-application, European locale and final acceptance gates remain open. The root integration owner will review and publish this branch separately.
|
||||
|
||||
Rollback is the inverse of the two source/test changes. There are no schema, dependency or persisted-data changes.
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
> job-tracker-ui@0.1.0 test
|
||||
> jest --runInBand job-discovery.test.tsx -t title sorting follows UI language changes
|
||||
|
||||
FAIL src/job-discovery.test.tsx
|
||||
● title sorting follows UI language changes without refetching or changing discovery filters
|
||||
|
||||
expect(received).toEqual(expected) // deep equality
|
||||
|
||||
- Expected - 1
|
||||
+ Received + 1
|
||||
|
||||
Array [
|
||||
- "Zoologist",
|
||||
"Åker agronom",
|
||||
+ "Zoologist",
|
||||
]
|
||||
|
||||
115 |
|
||||
116 | act(() => { window.dispatchEvent(new StorageEvent("storage", { key: "uiLanguage", newValue: "nb" })); });
|
||||
> 117 | expect(titles()).toEqual(["Zoologist", "Åker agronom"]);
|
||||
| ^
|
||||
118 | expect(screen.getByLabelText("Rolle eller bedrift")).toHaveValue("science");
|
||||
119 | expect(screen.getByLabelText("Kommune")).toHaveValue("Oslo");
|
||||
120 | expect(screen.getByRole("combobox", { name: translations.nb.jobDiscoverySort })).toHaveTextContent(translations.nb.jobDiscoverySortTitle);
|
||||
|
||||
at Object.toEqual (src/job-discovery.test.tsx:117:20)
|
||||
|
||||
Test Suites: 1 failed, 1 total
|
||||
Tests: 1 failed, 23 skipped, 24 total
|
||||
Snapshots: 0 total
|
||||
Time: 5.888 s
|
||||
Ran all test suites matching job-discovery.test.tsx with tests matching "title sorting follows UI language changes".
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
> job-tracker-ui@0.1.0 test
|
||||
> jest --runInBand job-discovery.test.tsx
|
||||
|
||||
Test Suites: 1 passed, 1 total
|
||||
Tests: 24 passed, 24 total
|
||||
Snapshots: 0 total
|
||||
Time: 8.499 s
|
||||
Ran all test suites matching job-discovery.test.tsx.
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
> job-tracker-ui@0.1.0 typecheck
|
||||
> tsc --noEmit --incremental false
|
||||
|
||||
@@ -320,7 +320,7 @@
|
||||
"docs/ui-modernisation/visual-correction/APPLICATION_ACTIONS_REVIEW.md": "4c6a12ddeb5d33521c6ac336492aeca3b0f115ebf08a1756d04b56f0593a4081",
|
||||
"docs/ui-modernisation/visual-correction/APPROVED_REFERENCE_MANIFEST.json": "50a9dfb53280ac44dc5e4fa1350441dab78d3ef6e66c3c0f448737c22efc6977",
|
||||
"docs/ui-modernisation/visual-correction/DISCREPANCIES.md": "db5accf7682d706cc83b8dae1ebf085b9c932deb0d875d72e144700e0e4b2812",
|
||||
"docs/ui-modernisation/visual-correction/ROUTE_COVERAGE.json": "db22044e6afbe753a1fadf37b1488b5cf8cbb49df5f236ee22f5e6e4055b5012",
|
||||
"docs/ui-modernisation/visual-correction/ROUTE_COVERAGE.json": "bbef6d0021496de3c533efb728bed282f5a7f126755b26f3347fbf4fb7e5a61d",
|
||||
"docs/ui-modernisation/visual-correction/evidence/admin-headings/processing-detail-en-dark-1024.png": "e4020cb49c37b486ed5e90b5ad7340ff3448b3c9232f10744e4a2821c48b3793",
|
||||
"docs/ui-modernisation/visual-correction/evidence/admin-headings/processing-detail-en-dark-1280.png": "3f05bdaebe605f541183277bb819903cd18ceb11b24f1339253012c137d2559e",
|
||||
"docs/ui-modernisation/visual-correction/evidence/admin-headings/processing-detail-en-dark-1440.png": "7bbe998ef813f6b1fea77b60769d6ccaaa3dcbd309ada4d783a1798b1a534e2a",
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
"JobTable"
|
||||
],
|
||||
"visualStatus": "FLAT_ROWS_CORRECTED_PENDING_FINAL_VISUAL_REVIEW",
|
||||
"afterStatus": "PASSED:36 configurations /72 title and explicit selection states at 1246994c. Earlier workspace, count, focus and Add Job evidence retained.",
|
||||
"afterStatus": "PASSED:36 configurations /108 ordinary, selection and row-menu states at bfbfaeb9. Earlier workspace, count, focus and Add Job evidence retained. Final visual and native acceptance remain open.",
|
||||
"functionalCriteria": "Original preservation register retained; no capability acceptance promotion",
|
||||
"evidence": "APPLICATION_ACTIONS_REVIEW.md; evidence/application-actions/BROWSER.json; earlier APPLICATION_SELECTION_REVIEW.md and APPLICATION_FLAT.md"
|
||||
},
|
||||
|
||||
@@ -97,6 +97,36 @@ test("sorts returned vacancies by title", async () => {
|
||||
expect(screen.getAllByRole("heading", { level: 3 }).map((element) => element.textContent)).toEqual(["Analyst", "Zoologist"]);
|
||||
});
|
||||
|
||||
test("title sorting follows UI language changes without refetching or changing discovery filters", async () => {
|
||||
searchResponse([
|
||||
{ id: "1", title: "Åker agronom", url: "https://example.test/1", source: "nav", countryCode: "NO" },
|
||||
{ id: "2", title: "Zoologist", url: "https://example.test/2", source: "nav", countryCode: "NO" },
|
||||
]);
|
||||
renderPage();
|
||||
fireEvent.change(screen.getByLabelText("Role or company"), { target: { value: "science" } });
|
||||
fireEvent.change(screen.getByLabelText("Municipality"), { target: { value: "Oslo" } });
|
||||
fireEvent.click(screen.getByRole("button", { name: "Search NAV" }));
|
||||
await screen.findByText("2 vacancies found");
|
||||
fireEvent.mouseDown(screen.getByRole("combobox", { name: "Sort results" }));
|
||||
fireEvent.click(await screen.findByRole("option", { name: "Title A–Z" }));
|
||||
const titles = () => screen.getAllByRole("heading", { level: 3 }).map((element) => element.textContent);
|
||||
expect(titles()).toEqual(["Åker agronom", "Zoologist"]);
|
||||
const reads = mockedApi.get.mock.calls.length;
|
||||
|
||||
act(() => { window.dispatchEvent(new StorageEvent("storage", { key: "uiLanguage", newValue: "nb" })); });
|
||||
expect(titles()).toEqual(["Zoologist", "Åker agronom"]);
|
||||
expect(screen.getByLabelText("Rolle eller bedrift")).toHaveValue("science");
|
||||
expect(screen.getByLabelText("Kommune")).toHaveValue("Oslo");
|
||||
expect(screen.getByRole("combobox", { name: translations.nb.jobDiscoverySort })).toHaveTextContent(translations.nb.jobDiscoverySortTitle);
|
||||
|
||||
act(() => { window.dispatchEvent(new StorageEvent("storage", { key: "uiLanguage", newValue: "en" })); });
|
||||
expect(titles()).toEqual(["Åker agronom", "Zoologist"]);
|
||||
expect(mockedApi.get).toHaveBeenCalledTimes(reads);
|
||||
expect(mockedApi.post).not.toHaveBeenCalled();
|
||||
expect(mockedApi.patch).not.toHaveBeenCalled();
|
||||
expect(mockedApi.delete).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("renders the discovery workflow in Norwegian Bokmål", async () => {
|
||||
window.localStorage.setItem("uiLanguage", "nb");
|
||||
renderPage();
|
||||
|
||||
@@ -171,16 +171,17 @@ export default function JobDiscoveryPage() {
|
||||
finally { pending.current = false; setMutating(false); }
|
||||
};
|
||||
|
||||
const locale = language === "nb" ? "nb-NO" : "en";
|
||||
const sortedJobs = useMemo(() => [...jobs].sort((left, right) => {
|
||||
if (sortOrder === "title") return left.title.localeCompare(right.title);
|
||||
if (sortOrder === "title") return left.title.localeCompare(right.title, locale);
|
||||
if (sortOrder === "deadline") {
|
||||
const leftDeadline = left.deadline ? new Date(left.deadline).getTime() : Number.MAX_SAFE_INTEGER;
|
||||
const rightDeadline = right.deadline ? new Date(right.deadline).getTime() : Number.MAX_SAFE_INTEGER;
|
||||
return leftDeadline - rightDeadline;
|
||||
}
|
||||
return new Date(right.modifiedAt || 0).getTime() - new Date(left.modifiedAt || 0).getTime();
|
||||
}), [jobs, sortOrder]);
|
||||
const formatDate = (value?: string) => value ? new Date(value).toLocaleDateString(language === "nb" ? "nb-NO" : "en") : null;
|
||||
}), [jobs, sortOrder, locale]);
|
||||
const formatDate = (value?: string) => value ? new Date(value).toLocaleDateString(locale) : null;
|
||||
|
||||
return (
|
||||
<Stack spacing={3} sx={{ minWidth: 0 }}>
|
||||
|
||||
Reference in New Issue
Block a user