diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3ff80884..f0873d1d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -48,32 +48,33 @@ jobs: - run: npm run test # disables because the action fails on github action but tests works locally - # e2e: - # runs-on: ubuntu-latest - # strategy: - # matrix: - # node-version: [18.x] - # steps: - # - name: Checkout - # uses: actions/checkout@v3 - # - name: Use Node.js ${{ matrix.node-version }} - # uses: actions/setup-node@v3 - # with: - # node-version: ${{ matrix.node-version }} - # - run: echo Using node version $(node --version) - # - run: echo Using npm version $(npm --version) - # - run: cypress/prepare-cypress-test.sh - # - name: Cypress run - # uses: cypress-io/github-action@v5 - # with: - # browser: chrome - # headed: true - # start: npm run develop -- -p 8000 - # wait-on: http://localhost:8000/index.de.html - # config: baseUrl=http://localhost:8000 + e2e: + runs-on: ubuntu-22.04 + container: + image: cypress/browsers:node-20.9.0-chrome-118.0.5993.88-1-ff-118.0.2-edge-118.0.2088.46-1 + strategy: + matrix: + node-version: [18.x] + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - run: echo Using node version $(node --version) + - run: echo Using npm version $(npm --version) + - run: cypress/prepare-cypress-test.sh + - name: Cypress run + uses: cypress-io/github-action@v6 + with: + headed: true + start: npm run develop -- -p 8000 + wait-on: 'http://0.0.0.0:8000' # see https://github.com/cypress-io/github-action#wait-on-with-nodejs-18 + config: baseUrl=http://localhost:8000 docker: - needs: [build] + needs: [build, e2e] if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }} runs-on: ubuntu-latest steps: diff --git a/README.md b/README.md index 8b97e6c8..aef41d99 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,12 @@ In general we advise the use of slash URIs for SKOS vocabularies. If you would like more support of hash URIs for SkoHub Vocabs, [please open an issue](https://github.com/skohub-io/skohub-vocabs/issues/new/choose). +## Internationalization + +To determine the language displayed of the vocabulary the browser language is used. +If the browser language is not present in the vocabulary a default language is chosen. +If you want to link to a specific language, you can use a URL parameter: `?lang=de`. + ## Set up ### Install Node.js @@ -125,6 +131,7 @@ You can configure the following settings: - Tokenizer used for searching - Custom Domain +- Fail on Validation - UI Configurations - Title - Logo @@ -148,6 +155,10 @@ Example: The base of your concept scheme is: `http://my-awesome-domain.org/my-vocab` Then provide `http://my-awesome-domain.org` as `custom_domain` in your `config.yaml` +### Fail on Validation + +If `true` (default) the build process will stop if a validation error occures. + ### UI The following customizations can be made: diff --git a/config.default.yaml b/config.default.yaml index 00935217..06fab5ef 100644 --- a/config.default.yaml +++ b/config.default.yaml @@ -2,6 +2,7 @@ # see https://github.com/nextapps-de/flexsearch#tokenizer-prefix-search for options tokenizer: "full" # strict, forward, reverse, full custom_domain: "" +fail_on_validation: true searchableAttributes: - "prefLabel" # you should not delete this one - "notation" diff --git a/cypress/config.e2e.yaml b/cypress/config.e2e.yaml new file mode 100644 index 00000000..4b2ceb4a --- /dev/null +++ b/cypress/config.e2e.yaml @@ -0,0 +1,38 @@ +--- +# see https://github.com/nextapps-de/flexsearch#tokenizer-prefix-search for options +tokenizer: "full" # strict, forward, reverse, full +custom_domain: "" +fail_on_validation: false +searchableAttributes: + - "prefLabel" # you should not delete this one + - "notation" + - "altLabel" + - "hiddenLabel" + - "example" + - "definition" +ui: + title: "SkoHub Vocabs" # Title is mandatory + logo: "skohub-signet-color.svg" # Path + colors: + skoHubWhite: "rgb(255, 255, 255)" + skoHubDarkColor: "rgb(15, 85, 75)" + skoHubMiddleColor: "rgb(20, 150, 140)" + skoHubLightColor: "rgb(40, 200, 175)" + skoHubThinColor: "rgb(55, 250, 210)" + skoHubBlackColor: "rgb(5, 30, 30)" + skoHubAction: "rgb(230, 0, 125)" + skoHubNotice: "rgb(250, 180, 50)" + skoHubDarkGrey: "rgb(155, 155, 155)" + skoHubMiddleGrey: "rgb(200, 200, 200)" + skoHubLightGrey: "rgb(235, 235, 235)" + fonts: + regular: + font_family: "Ubuntu" + font_style: "normal" + font_weight: 400 + name: "ubuntu-v20-latin-regular" + bold: + font_family: "Ubuntu" + font_style: "normal" + font_weight: 700 + name: "ubuntu-v20-latin-700" diff --git a/cypress/e2e/conceptSchemeAndConcept.cy.js b/cypress/e2e/conceptSchemeAndConcept.cy.js index ee8a82bf..97ec3fe5 100644 --- a/cypress/e2e/conceptSchemeAndConcept.cy.js +++ b/cypress/e2e/conceptSchemeAndConcept.cy.js @@ -2,35 +2,100 @@ describe("Concept Scheme and Concept", () => { it("Scrolling in Nested List and Concept on wide screen", () => { // we need a wider view to test scrolling in navbar and concept page cy.viewport(1280, 768) - cy.visit("/w3id.org/kim/hochschulfaechersystematik/n1.de.html") + cy.visit("/w3id.org/kim/hochschulfaechersystematik/n1.html") cy.findByRole("button", { name: "Expand" }).click() cy.get(".concepts").scrollTo("bottom") - cy.get("[id$=n1]").scrollTo("bottom") }) it("Scrolling in Nested List and Concept on smaller screen", () => { // we need a wider view to test scrolling in navbar and concept page cy.viewport(1024, 768) - cy.visit("/w3id.org/kim/hochschulfaechersystematik/n1.de.html") + cy.visit("/w3id.org/kim/hochschulfaechersystematik/n1.html") cy.findByRole("button", { name: "Expand" }).click() - cy.get(".concepts").scrollTo("bottom") + cy.scrollTo("bottom") }) it("Scrolling in Nested List and Concept on small screen", () => { // we need a wider view to test scrolling in navbar and concept page cy.viewport(800, 768) - cy.visit("/w3id.org/kim/hochschulfaechersystematik/n1.de.html") + cy.visit("/w3id.org/kim/hochschulfaechersystematik/n1.html") cy.findByRole("button", { name: "Expand" }).click() cy.get(".nav-block").scrollTo("bottom") cy.scrollTo("bottom") }) it("Copying URI works", () => { - cy.visit("/w3id.org/kim/hochschulfaechersystematik/n1.de.html") + cy.visit("/w3id.org/kim/hochschulfaechersystematik/n1.html") cy.get(".tooltip > button").click() cy.window() .its("navigator.clipboard") .then((clip) => clip.readText()) .should("equal", "https://w3id.org/kim/hochschulfaechersystematik/n1") }) + + it("Visting a slash URI Concept Scheme works", () => { + cy.visit("/w3id.org/index.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) + cy.get(".conceptScheme > a").should("have.text", "Test Vokabular") + cy.get("h1").should("have.text", "Test Vokabular") + cy.get(".markdown > span").should("have.text", "Test Beschreibung") + }) + + it("Visting a slash URI Concept works", () => { + cy.visit("/w3id.org/c1.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) + cy.get(".conceptScheme > a").should("have.text", "Test Vokabular") + cy.get("h1").should("include.text", "Konzept 1") + }) + + it("Visting a hash URI Concept Scheme works", () => { + cy.visit("/example.org/hashURIConceptScheme.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) + cy.get(".conceptScheme > a").should("have.text", "Hash URI Konzept Schema") + cy.get("h1").should("have.text", "Hash URI Konzept Schema") + }) + + it("Visting a hash URI Concept works", () => { + cy.visit("/example.org/hashURIConceptScheme.html#concept1", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) + cy.get(".conceptScheme > a").should("have.text", "Hash URI Konzept Schema") + cy.get("h1").should("have.text", "Konzept 1") + }) +}) + +describe("Parsing language from URL on Concept Schemes and Concepts", () => { + it("Visting a Concept Scheme directly with a language url param shows correct language", () => { + cy.visit("/w3id.org/kim/hochschulfaechersystematik/scheme.html?lang=de", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "en-EN" }) + }, + }) + cy.get(".conceptScheme > a").should( + "have.text", + "Destatis-Systematik der Fächergruppen, Studienbereiche und Studienfächer" + ) + }) + it("Visting a Concept directly with a language url param shows correct language", () => { + cy.visit("/w3id.org/kim/hochschulfaechersystematik/n1.html?lang=de", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "en-EN" }) + }, + }) + cy.get(".conceptScheme > a").should( + "have.text", + "Destatis-Systematik der Fächergruppen, Studienbereiche und Studienfächer" + ) + }) }) diff --git a/cypress/e2e/header.cy.js b/cypress/e2e/header.cy.js index ba14fd15..ba4bac4e 100644 --- a/cypress/e2e/header.cy.js +++ b/cypress/e2e/header.cy.js @@ -1,12 +1,21 @@ describe("Test header specific rendering", () => { it("shows only one concept scheme in header even when a concept has multiple", () => { - cy.visit("/w3id.org/two-concepts-one-file/c1.de.html") + cy.visit("/w3id.org/two-concepts-one-file/c1.html", { + // need to set browser language + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) cy.findByRole("link", { name: "Test Vokabular 1" }).should("exist") cy.findByRole("link", { name: "Test Vokabular 2" }).should("not.exist") }) it("when switching concepts there is only one concept scheme in header", () => { - cy.visit("/w3id.org/two-concepts-one-file/c2.de.html") + cy.visit("/w3id.org/two-concepts-one-file/c2.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) cy.findByRole("link", { name: "Test Vokabular 1" }).should("exist") cy.findByRole("link", { name: "Test Vokabular 2" }).should("not.exist") }) diff --git a/cypress/e2e/index.cy.js b/cypress/e2e/index.cy.js index dcd06a1c..74a7bbbe 100644 --- a/cypress/e2e/index.cy.js +++ b/cypress/e2e/index.cy.js @@ -1,7 +1,10 @@ describe("Main Vocab Index page", () => { it("Visits index page and test language switch", () => { - cy.visit("/index.de.html") - + cy.visit("/", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) // vocabs are found cy.get(".centerPage > ul li").should("have.length", 7) @@ -51,4 +54,51 @@ describe("Main Vocab Index page", () => { name: "Test Vokabular", }).should("exist") }) + + it("shows no concept scheme in header if going back to index page from concept", () => { + cy.visit("/", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) + cy.contains("Destatis-Systematik").click() + cy.findByRole("link", { + name: "Destatis-Systematik der Fächergruppen, Studienbereiche und Studienfächer", + }).should("exist") + cy.go("back") + cy.get(".conceptScheme > a").should("not.exist") + }) + + it("German language is selected, when lang=de param is given in url", () => { + cy.visit("/?lang=de", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "en-EN" }) + }, + }) + cy.findByRole("link", { + name: "Test Vokabular", + }).should("exist") + }) + + it("The navigator language is used as fallback language, when the language from url param 'lang' is not found", () => { + cy.visit("/?lang=bla", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "en-EN" }) + }, + }) + cy.findByRole("link", { + name: "Test Vocabulary", + }).should("exist") + }) + + it("A fallback language is used, when neither navigator language nor language from url param 'lang' is found", () => { + cy.visit("/?lang=bla", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "fr-FR" }) + }, + }) + cy.findByRole("link", { + name: "Test Vokabular", + }).should("exist") + }) }) diff --git a/cypress/e2e/languageTags.cy.js b/cypress/e2e/languageTags.cy.js index 8b04b165..6d19d48a 100644 --- a/cypress/e2e/languageTags.cy.js +++ b/cypress/e2e/languageTags.cy.js @@ -1,40 +1,60 @@ describe("Language Tags", () => { it("language tags get updated depending on the concept scheme coming from index page", () => { - cy.visit("/index.de.html") + cy.visit("/", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) cy.contains("Hash URI Konzept Schema").click() - cy.findByRole("link", { name: "en" }).should("not.exist") - cy.findByRole("link", { name: "de" }).should("not.exist") + cy.findByRole("button", { name: "en" }).should("not.exist") + cy.findByRole("button", { name: "de" }).should("not.exist") }) it("language tags shown on the concept scheme coming from index page", () => { - cy.visit("/index.de.html") + cy.visit("/", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) cy.contains("Test Vokabular").click() cy.get(".language-menu li").should("have.length", 2) - cy.findByRole("link", { name: "en" }).should("exist") + cy.findByRole("button", { name: "en" }).should("exist") }) it("language tags and concepts are present when visitng a concept directly", () => { - cy.visit("/w3id.org/index.de.html") + cy.visit("/w3id.org/index.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) cy.get(".language-menu li").should("have.length", 2) - cy.findByRole("link", { name: "en" }).should("exist") + cy.findByRole("button", { name: "en" }).should("exist") cy.get(".concepts").children().should("have.length", 1) }) it("language tags and concepts are present when visitng a collection directly", () => { - cy.visit("/w3id.org/collection.de.html") + cy.visit("/w3id.org/collection.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) cy.get(".language-menu li").should("have.length", 2) - cy.findByRole("link", { name: "en" }).should("exist") + cy.findByRole("button", { name: "en" }).should("exist") cy.get(".concepts").children().should("have.length", 1) }) it("switching languages keeps the concept tree and language tags ", () => { - cy.visit("/w3id.org/index.de.html") + cy.visit("/w3id.org/index.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) cy.contains("en").click() - cy.findByRole("link", { name: "de" }).should("exist") + cy.findByRole("button", { name: "de" }).should("exist") cy.get(".concepts").children().should("have.length", 1) }) }) diff --git a/cypress/e2e/modal.cy.js b/cypress/e2e/modal.cy.js index 2a79a475..26f3cb98 100644 --- a/cypress/e2e/modal.cy.js +++ b/cypress/e2e/modal.cy.js @@ -1,7 +1,11 @@ describe("modal", () => { // closing modal works it("click on close closes the modal", () => { - cy.visit("/w3id.org/index.de.html") + cy.visit("/w3id.org/index.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) cy.get("#settingsModal").should("not.be.visible") cy.get("#settings").click() cy.get("#settingsModal").should("be.visible") @@ -10,7 +14,11 @@ describe("modal", () => { }) it("click outside closes the modal", () => { - cy.visit("/w3id.org/index.de.html") + cy.visit("/w3id.org/index.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) cy.get("#settingsModal").should("not.be.visible") cy.get("#settings").click() cy.get("#settingsModal").should("be.visible") diff --git a/cypress/e2e/searchAndFilter.cy.js b/cypress/e2e/searchAndFilter.cy.js index 6e73b1c0..b64e0f0a 100644 --- a/cypress/e2e/searchAndFilter.cy.js +++ b/cypress/e2e/searchAndFilter.cy.js @@ -1,7 +1,11 @@ describe("search and filter", () => { // search and filter works it("search for top concept works", () => { - cy.visit("/w3id.org/index.de.html") + cy.visit("/w3id.org/index.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) cy.get("span").contains("Konzept 2").should("exist") cy.findByRole("textbox").type("Konzept 1") @@ -9,9 +13,33 @@ describe("search and filter", () => { cy.get("span").contains("Konzept 2").should("not.exist") }) - it("search for nested concept works", () => { - cy.visit("/w3id.org/index.de.html") + it("search for nested concept works (hash URIs)", () => { + cy.intercept("GET", "/example.org/hashURIConceptScheme-cs/search/**").as( + "getSearchIndices" + ) + cy.visit("/example.org/hashURIConceptScheme.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) + cy.wait("@getSearchIndices") + cy.get("span").contains("Konzept 2").should("exist") + cy.findByRole("textbox").type("Konzept 4") + cy.get("span").contains("Konzept 1").should("exist") + cy.get("span").contains("Konzept 2").should("not.exist") + cy.get("span").contains("Konzept 3").should("not.exist") + }) + + it("search for nested concept works (slash URIs)", () => { + cy.intercept("GET", "/w3id.org/index-cs/search/**").as("getSearchIndices") + cy.visit("/w3id.org/index.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) + + cy.wait("@getSearchIndices") cy.get("span").contains("Konzept 2").should("exist") cy.findByRole("textbox").type("Konzept 2") cy.get("span").contains("Konzept 1").should("exist") @@ -20,12 +48,19 @@ describe("search and filter", () => { }) it("search works after switching language", () => { - cy.visit("/w3id.org/index.de.html") + cy.intercept("GET", "/w3id.org/index-cs/search/**").as("getSearchIndices") + cy.visit("/w3id.org/index.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) - cy.contains("en").click().wait(0) // eslint-disable-line + cy.wait("@getSearchIndices") + cy.contains("en").click() + cy.wait("@getSearchIndices") - cy.get(".currentLanguage").contains("en").should("exist") - // cy.get("span").contains("Konzept 1").should("not.exist") + // cy.get(".currentLanguage").contains("en").should("exist") + cy.get("span").contains("Konzept 1").should("not.exist") cy.get("span").contains("Concept 1").should("exist") cy.findByRole("textbox").type("Concept 2") cy.get("span").contains("Concept 1").should("exist") @@ -34,18 +69,26 @@ describe("search and filter", () => { }) it("search works after switching concept schemes", () => { - cy.visit("/w3id.org/kim/hochschulfaechersystematik/scheme.en.html") + cy.visit("/w3id.org/kim/hochschulfaechersystematik/scheme.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "en-EN" }) + }, + }) cy.findByRole("textbox").type("Mathema") cy.get("span").contains("Mathematic").should("exist") - cy.visit("/w3id.org/cs-splitted-two-files/index.en.html") + cy.visit("/w3id.org/cs-splitted-two-files/index.html") cy.get("span").contains("Concept 1").should("exist") }) it("turning on altLabel checkbox returns altLabel matches", () => { - cy.visit("/w3id.org/index.de.html") + cy.visit("/w3id.org/index.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) cy.findByRole("textbox").type("Alternat") cy.get("p").contains("Nothing found").should("exist") cy.get("#settings").click() @@ -55,7 +98,11 @@ describe("search and filter", () => { }) it("turning on hiddenLabel checkbox returns hiddenLabel matches", () => { - cy.visit("/w3id.org/index.de.html") + cy.visit("/w3id.org/index.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) cy.findByRole("textbox").type("Verstecktes") cy.get("p").contains("Nothing found").should("exist") cy.get("#settings").click() @@ -64,7 +111,11 @@ describe("search and filter", () => { }) it("turning on notation checkbox returns notation matches", () => { - cy.visit("/w3id.org/index.de.html") + cy.visit("/w3id.org/index.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) cy.findByRole("textbox").type("Notat") cy.get("p").contains("Nothing found").should("exist") cy.get("#settings").click() @@ -74,7 +125,11 @@ describe("search and filter", () => { }) it("turning on definition checkbox returns definition matches", () => { - cy.visit("/w3id.org/index.de.html") + cy.visit("/w3id.org/index.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) cy.findByRole("textbox").type("Meine Defi") cy.get("p").contains("Nothing found").should("exist") cy.get("#settings").click() @@ -84,7 +139,11 @@ describe("search and filter", () => { }) it("turning on example checkbox returns example matches", () => { - cy.visit("/w3id.org/index.de.html") + cy.visit("/w3id.org/index.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) cy.findByRole("textbox").type("Beis") cy.get("p").contains("Nothing found").should("exist") cy.get("#settings").click() diff --git a/cypress/e2e/treeControls.cy.js b/cypress/e2e/treeControls.cy.js index 3d0dc83e..510ae783 100644 --- a/cypress/e2e/treeControls.cy.js +++ b/cypress/e2e/treeControls.cy.js @@ -1,12 +1,20 @@ describe("tree Controls", () => { it("tree controls not present in concept scheme with hierarchy", () => { - cy.visit("/purl.org/dcx/lrmi-vocabs/interactivityType/index.en.html") + cy.visit("/purl.org/dcx/lrmi-vocabs/interactivityType/index.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "en-EN" }) + }, + }) cy.contains("Collapse").should("not.exist") cy.contains("Expand").should("not.exist") }) it("tree controls present and working in concept scheme with hierarchy", () => { - cy.visit("/w3id.org/index.en.html") + cy.visit("/w3id.org/index.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "en-EN" }) + }, + }) cy.findByRole("button", { name: "Collapse" }).should("exist") cy.findByRole("button", { name: "Expand" }).should("exist") diff --git a/cypress/prepare-cypress-test.sh b/cypress/prepare-cypress-test.sh index 7727c939..516838f4 100755 --- a/cypress/prepare-cypress-test.sh +++ b/cypress/prepare-cypress-test.sh @@ -4,5 +4,13 @@ # and then copies the test files there find data/ -type f -not -name '.gitignore' -delete -cp test/data/ttl/*.ttl \ +cp test/data/ttl/hashURIConceptScheme.ttl \ + test/data/ttl/interactivityType.ttl \ + test/data/ttl/oneConceptSchemeTwoFiles_1.ttl \ + test/data/ttl/oneConceptSchemeTwoFiles_2.ttl \ + test/data/ttl/slashURIConceptScheme.ttl \ + test/data/ttl/systematik.ttl \ + test/data/ttl/twoConceptSchemesOneFile.ttl \ data/ + +cp cypress/config.e2e.yaml ./config.yaml diff --git a/gatsby-browser.js b/gatsby-browser.js index 3b2cdab3..61384741 100644 --- a/gatsby-browser.js +++ b/gatsby-browser.js @@ -12,5 +12,9 @@ export const wrapRootElement = ({ element }) => ( {element} ) -export const wrapPageElement = ({ element, props }) => - props.pageContext.node ? {element} : element +// if the pageContext contains node data, e.g. it's a concept scheme, +// concept or collection it gets wrapped in the App component +// otherwise the present page is delivered (in our case the index page) +export const wrapPageElement = ({ element, props }) => { + return props.pageContext.node ? {element} : element +} diff --git a/gatsby-config.js b/gatsby-config.js index 53c3e8ee..ffe32bfe 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -14,6 +14,7 @@ module.exports = { fonts: config.fonts, searchableAttributes: config.searchableAttributes, customDomain: config.customDomain, + failOnValidation: config.failOnValidation, }, pathPrefix: `${process.env.BASEURL || ""}`, plugins: [ diff --git a/gatsby-node.js b/gatsby-node.js index 9482ad50..1d995467 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -87,15 +87,9 @@ const getTurtleFiles = function (dirPath, arrayOfFiles) { **/ const exportIndex = (index, conceptScheme, language) => { index.export(function (key, data) { - const path = getFilePath( - (conceptScheme.id.endsWith("/") - ? conceptScheme.id.slice(0, -1) - : conceptScheme.id) + `-cs/search/${language}/${key}`, - `json`, - config.customDomain - ) + const path = getFilePath(conceptScheme.id) + `-cs/search/${language}/${key}` createData({ - path, + path: getFilePath(path, "json", config.customDomain), data: data !== undefined ? data : "", }) }) @@ -112,12 +106,14 @@ exports.onPreBootstrap = async ({ createContentDigest, actions, getNode }) => { console.info(`Found these turtle files:`) ttlFiles.forEach((e) => console.info(e)) for (const f of ttlFiles) { - try { - console.info("Validating: ", f) - await validate("shapes/skohub.shacl.ttl", f) - } catch (e) { - console.error(e) - throw e + if (config.failOnValidation) { + try { + console.info("Validating: ", f) + await validate("shapes/skohub.shacl.ttl", f) + } catch (e) { + console.error(e) + throw e + } } const ttlString = fs.readFileSync(f).toString() const doc = await jsonld.fromRDF(ttlString, { format: "text/turtle" }) @@ -247,21 +243,14 @@ exports.createPages = async ({ graphql, actions: { createPage } }) => { const json = omitEmpty(Object.assign({}, collection, context.jsonld)) const jsonld = omitEmpty(Object.assign({}, collection, context.jsonld)) - languages.forEach((language) => - createPage({ - path: getFilePath( - collection.id, - `${language}.html`, - config.customDomain - ), - component: path.resolve(`./src/components/Collection.jsx`), - context: { - language, - node: collection, - customDomain: config.customDomain, - }, - }) - ) + createPage({ + path: getFilePath(collection.id, `html`, config.customDomain), + component: path.resolve(`./src/components/Collection.jsx`), + context: { + node: collection, + customDomain: config.customDomain, + }, + }) createData({ path: getFilePath(collection.id, "json", config.customDomain), data: JSON.stringify(json, null, 2), @@ -331,24 +320,17 @@ exports.createPages = async ({ graphql, actions: { createPage } }) => { embeddedConcepts.push({ json, jsonld }) } else { // create pages and data - languagesOfCS.forEach((language) => - createPage({ - path: getFilePath( - concept.id, - `${language}.html`, - config.customDomain - ), - component: path.resolve(`./src/components/Concept.jsx`), - context: { - language, - node: concept, - collections: memberOf.hasOwnProperty(concept.id) - ? memberOf[concept.id] - : [], - customDomain: config.customDomain, - }, - }) - ) + createPage({ + path: getFilePath(concept.id, `html`, config.customDomain), + component: path.resolve(`./src/components/Concept.jsx`), + context: { + node: concept, + collections: memberOf.hasOwnProperty(concept.id) + ? memberOf[concept.id] + : [], + customDomain: config.customDomain, + }, + }) createData({ path: getFilePath(concept.id, "json", config.customDomain), data: JSON.stringify(json, null, 2), @@ -384,22 +366,16 @@ exports.createPages = async ({ graphql, actions: { createPage } }) => { indexes[language].add(document) }) }) - languagesOfCS.forEach((language) => - createPage({ - path: getFilePath( - conceptScheme.id, - `${language}.html`, - config.customDomain - ), - component: path.resolve(`./src/components/ConceptScheme.jsx`), - context: { - language, - node: conceptScheme, - embed: embeddedConcepts, - customDomain: config.customDomain, - }, - }) - ) + createPage({ + path: getFilePath(conceptScheme.id, `html`, config.customDomain), + component: path.resolve(`./src/components/ConceptScheme.jsx`), + context: { + node: conceptScheme, + embed: embeddedConcepts, + customDomain: config.customDomain, + }, + }) + createData({ path: getFilePath(conceptScheme.id, "json", config.customDomain), data: JSON.stringify( @@ -419,26 +395,18 @@ exports.createPages = async ({ graphql, actions: { createPage } }) => { } ) ) - - // Build index pages - languages.forEach((language) => - createPage({ - path: `/index.${language}.html`, - component: path.resolve(`./src/components/index.jsx`), - context: { - language, - conceptSchemes: conceptSchemes.data.allConceptScheme.edges.map( - (node) => node.node - ), - languagesByCS: Object.fromEntries( - Object.entries(languagesByCS).map(([key, value]) => { - return [key, Array.from(value)] - }) - ), - customDomain: config.customDomain, - }, - }) + const indexData = await Promise.all( + conceptSchemes.data.allConceptScheme.edges.map(({ node: cs }) => ({ + id: cs.id, + title: cs.title, + description: cs.description, + languages: Array.from(languagesByCS[cs.id]), + })) ) + createData({ + path: getFilePath("/index", "json", config.customDomain), + data: JSON.stringify(indexData), + }) } exports.onCreateWebpackConfig = ({ actions }) => { diff --git a/package-lock.json b/package-lock.json index b4f42cc9..4b0fd4fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,13 +40,13 @@ }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", - "@testing-library/cypress": "^9.0.0", + "@testing-library/cypress": "^10.0.1", "@testing-library/jest-dom": "^6.0.0", "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.4.3", "@vitejs/plugin-react": "^4.0.4", "@vitest/coverage-v8": "^0.34.2", - "cypress": "^12.3.0", + "cypress": "^13.4.0", "eslint": "^8.29.0", "eslint-config-react-app": "^7.0.0", "eslint-plugin-cypress": "^2.12.1", @@ -54,7 +54,7 @@ "husky": "^8.0.2", "identity-obj-proxy": "^3.0.0", "jsdom": "^22.1.0", - "lint-staged": "^14.0.0", + "lint-staged": "^15.0.2", "prettier": "^3.0.2", "start-server-and-test": "^2.0.0", "vitest": "^0.34.1" @@ -2206,9 +2206,9 @@ } }, "node_modules/@cypress/request": { - "version": "2.88.12", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.12.tgz", - "integrity": "sha512-tOn+0mDZxASFM+cuAP9szGUGPI1HwWVSvdzm7V4cCsPdFTx6qMj29CwaQmRAMIEhORIUBFBsYROYJcveK4uOjA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.1.tgz", + "integrity": "sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==", "dev": true, "dependencies": { "aws-sign2": "~0.7.0", @@ -2224,7 +2224,7 @@ "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", - "qs": "~6.10.3", + "qs": "6.10.4", "safe-buffer": "^5.1.2", "tough-cookie": "^4.1.3", "tunnel-agent": "^0.6.0", @@ -2769,9 +2769,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", - "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", + "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -2807,9 +2807,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz", - "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz", + "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2824,14 +2824,14 @@ } }, "node_modules/@gatsbyjs/parcel-namer-relative-to-cwd": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/@gatsbyjs/parcel-namer-relative-to-cwd/-/parcel-namer-relative-to-cwd-2.12.0.tgz", - "integrity": "sha512-ENTps2Fg3EMy5WyTrX3TY62McmZcyhJbU/rD90UTyqZnB9XbuEFNW72Ya6qH0jXMPV7tcxNeD2P7HijuJ1O5lQ==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/@gatsbyjs/parcel-namer-relative-to-cwd/-/parcel-namer-relative-to-cwd-2.12.1.tgz", + "integrity": "sha512-DYtRRu0yhs/T3eWtOsuJK8qG5+TPfMnbB3q20hYOxsm6BnOuIUYIHNmZNlP7VcrBTCCZJUW/6xhq81mA6GvHWA==", "dependencies": { "@babel/runtime": "^7.20.13", "@parcel/namer-default": "2.8.3", "@parcel/plugin": "2.8.3", - "gatsby-core-utils": "^4.12.0" + "gatsby-core-utils": "^4.12.1" }, "engines": { "node": ">=18.0.0", @@ -3304,12 +3304,12 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", - "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", + "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", "minimatch": "^3.0.5" }, @@ -3317,6 +3317,12 @@ "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/config-array/node_modules/@humanwhocodes/object-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -4942,26 +4948,26 @@ } }, "node_modules/@testing-library/cypress": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@testing-library/cypress/-/cypress-9.0.0.tgz", - "integrity": "sha512-c1XiCGeHGGTWn0LAU12sFUfoX3qfId5gcSE2yHode+vsyHDWraxDPALjVnHd4/Fa3j4KBcc5k++Ccy6A9qnkMA==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@testing-library/cypress/-/cypress-10.0.1.tgz", + "integrity": "sha512-e8uswjTZIBhaIXjzEcrQQ8nHRWHgZH7XBxKuIWxZ/T7FxfWhCR48nFhUX5nfPizjVOKSThEfOSv67jquc1ASkw==", "dev": true, "dependencies": { "@babel/runtime": "^7.14.6", - "@testing-library/dom": "^8.1.0" + "@testing-library/dom": "^9.0.0" }, "engines": { "node": ">=12", "npm": ">=6" }, "peerDependencies": { - "cypress": "^12.0.0" + "cypress": "^12.0.0 || ^13.0.0" } }, "node_modules/@testing-library/dom": { - "version": "8.20.1", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz", - "integrity": "sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==", + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.3.tgz", + "integrity": "sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.10.4", @@ -4974,7 +4980,7 @@ "pretty-format": "^27.0.2" }, "engines": { - "node": ">=12" + "node": ">=14" } }, "node_modules/@testing-library/jest-dom": { @@ -5049,25 +5055,6 @@ "react-dom": "^18.0.0" } }, - "node_modules/@testing-library/react/node_modules/@testing-library/dom": { - "version": "9.3.3", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.3.tgz", - "integrity": "sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.1.3", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/@testing-library/user-event": { "version": "14.5.1", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.1.tgz", @@ -5205,9 +5192,9 @@ } }, "node_modules/@types/common-tags": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@types/common-tags/-/common-tags-1.8.3.tgz", - "integrity": "sha512-v3smfzf7umSwpkJrmlUe+apSv6bVnrIFCeBeprnP4f8lIh6pECZxyD50e8yFwfouIt85TdxN5yXiFwS5fnsS3w==" + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/@types/common-tags/-/common-tags-1.8.4.tgz", + "integrity": "sha512-S+1hLDJPjWNDhcGxsxEbepzaxWqURP/o+3cP4aa2w7yBXgdcmKGQtZzP8JbyfOd0m+33nh+8+kvxYE2UJtBDkg==" }, "node_modules/@types/configstore": { "version": "2.1.1", @@ -5325,14 +5312,17 @@ } }, "node_modules/@types/node": { - "version": "16.18.59", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.59.tgz", - "integrity": "sha512-PJ1w2cNeKUEdey4LiPra0ZuxZFOGvetswE8qHRriV/sUkL5Al4tTmPV9D2+Y/TPIxTHHgxTfRjZVKWhPw/ORhQ==" + "version": "18.18.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.8.tgz", + "integrity": "sha512-OLGBaaK5V3VRBS1bAkMVP2/W9B+H8meUfl866OrMNQqt7wDgdpWPp5o6gmIc9pB+lIQHSq4ZL8ypeH1vPxcPaQ==", + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-lX17GZVpJ/fuCjguZ5b3TjEbSENxmEk1B2z02yoXSK9WMEWRivhdSY73wWMn6bpcCDAOh6qAdktpKHIlkDk2lg==", + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.8.tgz", + "integrity": "sha512-nnH5lV9QCMPsbEVdTb5Y+F3GQxLSw1xQgIydrb2gSfEavRPs50FnMr+KUaa+LoPSqibm2N+ZZxH7lavZlAT4GA==", "dependencies": { "@types/node": "*", "form-data": "^4.0.0" @@ -5370,9 +5360,9 @@ } }, "node_modules/@types/reach__router": { - "version": "1.3.12", - "resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.12.tgz", - "integrity": "sha512-DyDionxE76G/48FclCcQ5CMigX9FTMfbcMMNvsZ4ti1ZR33wEOgiIVDgVsHEDv1bEi6FfTBQ9aRF4LSELUe+CQ==", + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.13.tgz", + "integrity": "sha512-1vbCXT/emPNCd7Pywr9mPp/FsdTwUfxHeMx0k3Rpz4ghPr/k5W1rejbg724BbkC8LkehS6y6mf3wOfrApA4NrA==", "dependencies": { "@types/react": "*" } @@ -5764,6 +5754,12 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/@vercel/webpack-asset-relocator-loader": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/@vercel/webpack-asset-relocator-loader/-/webpack-asset-relocator-loader-1.7.3.tgz", @@ -5773,15 +5769,15 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.1.0.tgz", - "integrity": "sha512-rM0SqazU9iqPUraQ2JlIvReeaxOoRj6n+PzB1C0cBzIbd8qP336nC39/R9yPi3wVcah7E7j/kdU1uCUqMEU4OQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.1.1.tgz", + "integrity": "sha512-Jie2HERK+uh27e+ORXXwEP5h0Y2lS9T2PRGbfebiHGlwzDO0dEnd2aNtOR/qjBlPb1YgxwAONeblL1xqLikLag==", "dev": true, "dependencies": { - "@babel/core": "^7.22.20", + "@babel/core": "^7.23.2", "@babel/plugin-transform-react-jsx-self": "^7.22.5", "@babel/plugin-transform-react-jsx-source": "^7.22.5", - "@types/babel__core": "^7.20.2", + "@types/babel__core": "^7.20.3", "react-refresh": "^0.14.0" }, "engines": { @@ -6841,13 +6837,13 @@ } }, "node_modules/babel-plugin-remove-graphql-queries": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/babel-plugin-remove-graphql-queries/-/babel-plugin-remove-graphql-queries-5.12.0.tgz", - "integrity": "sha512-9lLU6pYKtS0tSfqzehAHp1ODgTzOtoZPIkVfmjCTxATtEmgCO+Z8tptdW+pcp2cKEs9BIJ6SuOkE47SEXxVeiw==", + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/babel-plugin-remove-graphql-queries/-/babel-plugin-remove-graphql-queries-5.12.1.tgz", + "integrity": "sha512-R5FyZLs+YfhCpUJkpSyVwIbaw9Ya4TC4xIOBJzPK9Z3u5XVCI459aykLPyfYAWwbsI9yvjm/Ux5ft4/U4rNvMQ==", "dependencies": { "@babel/runtime": "^7.20.13", "@babel/types": "^7.20.7", - "gatsby-core-utils": "^4.12.0" + "gatsby-core-utils": "^4.12.1" }, "engines": { "node": ">=18.0.0" @@ -6905,9 +6901,9 @@ } }, "node_modules/babel-preset-gatsby": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/babel-preset-gatsby/-/babel-preset-gatsby-3.12.0.tgz", - "integrity": "sha512-355xQi5cZIFYCdCbeJ9mHipEZrk0Jmk5zq21Kd4FlYAAYn0L8lC/YiSG44muRyPDdSVJiLOr6pMGOWQRe5PPyg==", + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/babel-preset-gatsby/-/babel-preset-gatsby-3.12.1.tgz", + "integrity": "sha512-M3q7TB9YOpILjyd4ShjvWG0Agzjapu+FPQUczy9iBxlzVPcAe5hiPRlEin1v0CvXrlwj+GNydrhlczCPaf8YkA==", "dependencies": { "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", @@ -6922,7 +6918,7 @@ "babel-plugin-dynamic-import-node": "^2.3.3", "babel-plugin-macros": "^3.1.0", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", - "gatsby-core-utils": "^4.12.0", + "gatsby-core-utils": "^4.12.1", "gatsby-legacy-polyfills": "^3.12.0" }, "engines": { @@ -7217,19 +7213,22 @@ } }, "node_modules/browserify-sign": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", - "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz", + "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==", "dependencies": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", + "bn.js": "^5.2.1", + "browserify-rsa": "^4.1.0", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", - "elliptic": "^6.5.3", + "elliptic": "^6.5.4", "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" + "parse-asn1": "^5.1.6", + "readable-stream": "^3.6.2", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 4" } }, "node_modules/browserify-sign/node_modules/readable-stream": { @@ -8281,9 +8280,9 @@ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, "node_modules/create-gatsby": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/create-gatsby/-/create-gatsby-3.12.1.tgz", - "integrity": "sha512-bDggOdWUu97L6BEdt22QJRi14Wkm+vvVSqozqFmL45DGYTsJ2Ckogf2VH0nTlq8EXCmqTxaKyvp66hbd44afsw==", + "version": "3.12.3", + "resolved": "https://registry.npmjs.org/create-gatsby/-/create-gatsby-3.12.3.tgz", + "integrity": "sha512-N0K/Z/MD5LMRJcBy669WpSgrn+31zBV72Lv0RHolX0fXa77Yx58HsEiLWz83j/dtciGMQfEOEHFRetUqZhOggA==", "dependencies": { "@babel/runtime": "^7.20.13" }, @@ -8638,15 +8637,15 @@ "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, "node_modules/cypress": { - "version": "12.17.4", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-12.17.4.tgz", - "integrity": "sha512-gAN8Pmns9MA5eCDFSDJXWKUpaL3IDd89N9TtIupjYnzLSmlpVr+ZR+vb4U/qaMp+lB6tBvAmt7504c3Z4RU5KQ==", + "version": "13.4.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.4.0.tgz", + "integrity": "sha512-KeWNC9xSHG/ewZURVbaQsBQg2mOKw4XhjJZFKjWbEjgZCdxpPXLpJnfq5Jns1Gvnjp6AlnIfpZfWFlDgVKXdWQ==", "dev": true, "hasInstallScript": true, "dependencies": { - "@cypress/request": "2.88.12", + "@cypress/request": "^3.0.0", "@cypress/xvfb": "^1.2.4", - "@types/node": "^16.18.39", + "@types/node": "^18.17.5", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", "arch": "^2.2.0", @@ -8692,7 +8691,7 @@ "cypress": "bin/cypress" }, "engines": { - "node": "^14.0.0 || ^16.0.0 || >=18.0.0" + "node": "^16.0.0 || ^18.0.0 || >=20.0.0" } }, "node_modules/cypress/node_modules/fs-extra": { @@ -9533,9 +9532,9 @@ } }, "node_modules/envinfo": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.10.0.tgz", - "integrity": "sha512-ZtUjZO6l5mwTHvc1L9+1q5p/R3wTopcfqMW8r5t8SJSKqeVI/LtajORwRFEKpEFuekjD0VBjwu1HMxL4UalIRw==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.11.0.tgz", + "integrity": "sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg==", "bin": { "envinfo": "dist/cli.js" }, @@ -9809,18 +9808,19 @@ } }, "node_modules/eslint": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz", - "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", + "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.51.0", - "@humanwhocodes/config-array": "^0.11.11", + "@eslint/eslintrc": "^2.1.3", + "@eslint/js": "8.53.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -11284,9 +11284,9 @@ } }, "node_modules/gatsby": { - "version": "5.12.7", - "resolved": "https://registry.npmjs.org/gatsby/-/gatsby-5.12.7.tgz", - "integrity": "sha512-KKwV+GHCLykXKevG2UbyYPGYHA3LaFWtWszJIr3V7W7aUDQ1GnO0oaf6myUvgmDJ/p4Ks0On/QErJSxjuGlciA==", + "version": "5.12.9", + "resolved": "https://registry.npmjs.org/gatsby/-/gatsby-5.12.9.tgz", + "integrity": "sha512-Nu+A4lW/7VDcxdaBypbp/Sw9mEzpfVnDBiwb0yiSW3pZDpC7iKFVVlhK2Ovq6Wy4gyYiE1LjT2sMNGKR1lkh6Q==", "hasInstallScript": true, "dependencies": { "@babel/code-frame": "^7.18.6", @@ -11327,8 +11327,8 @@ "babel-plugin-add-module-exports": "^1.0.4", "babel-plugin-dynamic-import-node": "^2.3.3", "babel-plugin-lodash": "^3.3.4", - "babel-plugin-remove-graphql-queries": "^5.12.0", - "babel-preset-gatsby": "^3.12.0", + "babel-plugin-remove-graphql-queries": "^5.12.1", + "babel-preset-gatsby": "^3.12.1", "better-opn": "^2.1.1", "bluebird": "^3.7.2", "body-parser": "1.20.1", @@ -11370,19 +11370,19 @@ "find-cache-dir": "^3.3.2", "fs-exists-cached": "1.0.0", "fs-extra": "^11.1.1", - "gatsby-cli": "^5.12.2", - "gatsby-core-utils": "^4.12.0", + "gatsby-cli": "^5.12.4", + "gatsby-core-utils": "^4.12.1", "gatsby-graphiql-explorer": "^3.12.1", "gatsby-legacy-polyfills": "^3.12.0", - "gatsby-link": "^5.12.0", - "gatsby-page-utils": "^3.12.0", - "gatsby-parcel-config": "1.12.0", - "gatsby-plugin-page-creator": "^5.12.1", - "gatsby-plugin-typescript": "^5.12.0", - "gatsby-plugin-utils": "^4.12.1", + "gatsby-link": "^5.12.1", + "gatsby-page-utils": "^3.12.1", + "gatsby-parcel-config": "^1.12.1", + "gatsby-plugin-page-creator": "^5.12.3", + "gatsby-plugin-typescript": "^5.12.1", + "gatsby-plugin-utils": "^4.12.3", "gatsby-react-router-scroll": "^6.12.0", "gatsby-script": "^2.12.0", - "gatsby-telemetry": "^4.12.0", + "gatsby-telemetry": "^4.12.1", "gatsby-worker": "^2.12.0", "glob": "^7.2.3", "globby": "^11.1.0", @@ -11471,9 +11471,9 @@ } }, "node_modules/gatsby-cli": { - "version": "5.12.2", - "resolved": "https://registry.npmjs.org/gatsby-cli/-/gatsby-cli-5.12.2.tgz", - "integrity": "sha512-Eqg7BzHhWgzlaovVQR4EqEMaQ94WVLV4tzmSFADI1my8Q0byUAoQ5lVMwRP6mcX1nIhZ3Ut045Ty2y91amF7xw==", + "version": "5.12.4", + "resolved": "https://registry.npmjs.org/gatsby-cli/-/gatsby-cli-5.12.4.tgz", + "integrity": "sha512-GD+otyd5LlgSbYK4ODrKyAise/k32G7Qy7H/k+gJ2P8DCG9sU+j//2zNwF7mY8C5dl0SpROqFTL+I0Y1DK4tmQ==", "hasInstallScript": true, "dependencies": { "@babel/code-frame": "^7.18.6", @@ -11492,13 +11492,13 @@ "clipboardy": "^2.3.0", "common-tags": "^1.8.2", "convert-hrtime": "^3.0.0", - "create-gatsby": "^3.12.1", + "create-gatsby": "^3.12.3", "envinfo": "^7.10.0", "execa": "^5.1.1", "fs-exists-cached": "^1.0.0", "fs-extra": "^11.1.1", - "gatsby-core-utils": "^4.12.0", - "gatsby-telemetry": "^4.12.0", + "gatsby-core-utils": "^4.12.1", + "gatsby-telemetry": "^4.12.1", "hosted-git-info": "^3.0.8", "is-valid-path": "^0.1.1", "joi": "^17.9.2", @@ -11597,9 +11597,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/gatsby-core-utils": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/gatsby-core-utils/-/gatsby-core-utils-4.12.0.tgz", - "integrity": "sha512-1vK0cmL8FNHAddQ5WZt0yTPdFSZuMPNUSsHckM+ZdVmRxyif3aZYSi7ofj6sJo/UvhKj7fBqJv/smZYpp2PRqg==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/gatsby-core-utils/-/gatsby-core-utils-4.12.1.tgz", + "integrity": "sha512-YW7eCK2M6yGQerT5LkdOHLZTNYMsDvcgeDMRy0q66FWKj7twPZX428I6NaLCMeF5dYoj1HOOO0u96iNlW5jcKQ==", "dependencies": { "@babel/runtime": "^7.20.13", "ci-info": "2.0.0", @@ -11652,12 +11652,12 @@ } }, "node_modules/gatsby-link": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/gatsby-link/-/gatsby-link-5.12.0.tgz", - "integrity": "sha512-Ky7q6zeminbKQpSYjKW8YkkbV8+b01MUM/WNtGDMzu6jOfYFvv0+2kJXTxF0mzyAJMq9Xx44yFK0LX5kLF5fZA==", + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/gatsby-link/-/gatsby-link-5.12.1.tgz", + "integrity": "sha512-0xhQhRnpPRHWouoNzkVTu8qhbUa8GhbRrCo2QKiOyAdVzU96ZzWEMw2FUkgG6Ht5kglDXHek6LOiWyAv6jf49g==", "dependencies": { "@types/reach__router": "^1.3.10", - "gatsby-page-utils": "^3.12.0", + "gatsby-page-utils": "^3.12.1", "prop-types": "^15.8.1" }, "engines": { @@ -11670,15 +11670,15 @@ } }, "node_modules/gatsby-page-utils": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/gatsby-page-utils/-/gatsby-page-utils-3.12.0.tgz", - "integrity": "sha512-xkwGE3qf+wzpI0Y7dQyZlWFC7HL7O6eTZ2DrkbIUPAyq3nWSJQnbuhZ9KaPBK3Qs955T8/iUeHU9YQu3Y5Wcgg==", + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/gatsby-page-utils/-/gatsby-page-utils-3.12.1.tgz", + "integrity": "sha512-BGtAvx4JZ143uRHYlUbWS8ZjOJ14fpj3nQfb68y9ZsNL1gdwjdWjuPXTM1gQ+w6wXDsHD/ovmYz1ZHG7qrQjJQ==", "dependencies": { "@babel/runtime": "^7.20.13", "bluebird": "^3.7.2", "chokidar": "^3.5.3", "fs-exists-cached": "^1.0.0", - "gatsby-core-utils": "^4.12.0", + "gatsby-core-utils": "^4.12.1", "glob": "^7.2.3", "lodash": "^4.17.21", "micromatch": "^4.0.5" @@ -11688,11 +11688,11 @@ } }, "node_modules/gatsby-parcel-config": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/gatsby-parcel-config/-/gatsby-parcel-config-1.12.0.tgz", - "integrity": "sha512-Ouru3TuIadzcTJ3zC943V3TKaesKOB0PW07dAlvXnjX8BXLDz9bgaS+eQlq+kvIrE1bsdpIWwAo/eL58a2+RCg==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/gatsby-parcel-config/-/gatsby-parcel-config-1.12.1.tgz", + "integrity": "sha512-hH9m/dSJTkdeksBzLGi9U+Pey0CsPeHHrRP6pugxd7owtJUQqid37noyadqnawBo2LOwcGE4o69HhqGxGNXxbw==", "dependencies": { - "@gatsbyjs/parcel-namer-relative-to-cwd": "^2.12.0", + "@gatsbyjs/parcel-namer-relative-to-cwd": "^2.12.1", "@parcel/bundler-default": "2.8.3", "@parcel/compressor-raw": "2.8.3", "@parcel/namer-default": "2.8.3", @@ -11730,22 +11730,22 @@ } }, "node_modules/gatsby-plugin-image": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/gatsby-plugin-image/-/gatsby-plugin-image-3.12.1.tgz", - "integrity": "sha512-OJ3PGxBp5WbadGFZtd1VqMOB3d8ktysgtkFZbHwLUuGqxFKFMuySBS/kFudohcMJSeCIEgK9pEzWJ1RWh3mDWw==", + "version": "3.12.3", + "resolved": "https://registry.npmjs.org/gatsby-plugin-image/-/gatsby-plugin-image-3.12.3.tgz", + "integrity": "sha512-945XpVY/14M9msfOI2fulunEUSJnw0YnwfbRq7omvqAWOH/fCnXWkyYj89NAcmNIOSM/a+KgQyaIDosnVHXdpw==", "dependencies": { "@babel/code-frame": "^7.18.6", "@babel/parser": "^7.20.13", "@babel/runtime": "^7.20.13", "@babel/traverse": "^7.20.13", "babel-jsx-utils": "^1.1.0", - "babel-plugin-remove-graphql-queries": "^5.12.0", + "babel-plugin-remove-graphql-queries": "^5.12.1", "camelcase": "^6.3.0", "chokidar": "^3.5.3", "common-tags": "^1.8.2", "fs-extra": "^11.1.1", - "gatsby-core-utils": "^4.12.0", - "gatsby-plugin-utils": "^4.12.1", + "gatsby-core-utils": "^4.12.1", + "gatsby-plugin-utils": "^4.12.3", "objectFitPolyfill": "^2.3.5", "prop-types": "^15.8.1" }, @@ -11767,13 +11767,13 @@ } }, "node_modules/gatsby-plugin-manifest": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/gatsby-plugin-manifest/-/gatsby-plugin-manifest-5.12.1.tgz", - "integrity": "sha512-S/t63Suh8zN0hlTnAioIeVILY8/IGfnw7+n/fhn39jPdog7fuvECIJeUHYU1X3JYYCfQHMjdHdJ4hfTa6m5YdA==", + "version": "5.12.3", + "resolved": "https://registry.npmjs.org/gatsby-plugin-manifest/-/gatsby-plugin-manifest-5.12.3.tgz", + "integrity": "sha512-qpH0pSIIt7ggO7OnP127eKn6fhD1DKTzg9Aw8vaMCO8MMOQ5qfOn3ZrRCgH6DuaU1admZU18gFKlCKH+QHoGfQ==", "dependencies": { "@babel/runtime": "^7.20.13", - "gatsby-core-utils": "^4.12.0", - "gatsby-plugin-utils": "^4.12.1", + "gatsby-core-utils": "^4.12.1", + "gatsby-plugin-utils": "^4.12.3", "semver": "^7.5.3", "sharp": "^0.32.6" }, @@ -11815,9 +11815,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/gatsby-plugin-page-creator": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/gatsby-plugin-page-creator/-/gatsby-plugin-page-creator-5.12.1.tgz", - "integrity": "sha512-qyGfxNG5D0ICxgGBeK6vgAIyj+xaHXc/fXLElHmfB0G+IauywMAFY9GKg6Kd0VYGqtDNTrtLlNaquI6DNJZw8Q==", + "version": "5.12.3", + "resolved": "https://registry.npmjs.org/gatsby-plugin-page-creator/-/gatsby-plugin-page-creator-5.12.3.tgz", + "integrity": "sha512-li9jKy70h4vXNxxRrXP2DpgEx05m5E7EDOLCjAWNsm7e9EO1szixXQ0ev6Ie1SBKT6vAHAoIonet6+oFattf9w==", "dependencies": { "@babel/runtime": "^7.20.13", "@babel/traverse": "^7.20.13", @@ -11825,10 +11825,10 @@ "chokidar": "^3.5.3", "fs-exists-cached": "^1.0.0", "fs-extra": "^11.1.1", - "gatsby-core-utils": "^4.12.0", - "gatsby-page-utils": "^3.12.0", - "gatsby-plugin-utils": "^4.12.1", - "gatsby-telemetry": "^4.12.0", + "gatsby-core-utils": "^4.12.1", + "gatsby-page-utils": "^3.12.1", + "gatsby-plugin-utils": "^4.12.3", + "gatsby-telemetry": "^4.12.1", "globby": "^11.1.0", "lodash": "^4.17.21" }, @@ -11840,9 +11840,9 @@ } }, "node_modules/gatsby-plugin-sharp": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/gatsby-plugin-sharp/-/gatsby-plugin-sharp-5.12.1.tgz", - "integrity": "sha512-IJFBL+m1H5RMs4rRiGiZa5NreKxRzycUzAVj17upxs3DTUysSlwoIORGEYJfIJP+bM2p7RjMccpEAqcrXQ8K5g==", + "version": "5.12.3", + "resolved": "https://registry.npmjs.org/gatsby-plugin-sharp/-/gatsby-plugin-sharp-5.12.3.tgz", + "integrity": "sha512-bEVhap/Ce6pzXXk/9U6Xug9+MmXwpZKzQZke8PxNBy1ouMZ2H4chgpcASnWpwnWkPvP+Fnehlh/whySDHmoLGw==", "dependencies": { "@babel/runtime": "^7.20.13", "async": "^3.2.4", @@ -11850,8 +11850,8 @@ "debug": "^4.3.4", "filenamify": "^4.3.0", "fs-extra": "^11.1.1", - "gatsby-core-utils": "^4.12.0", - "gatsby-plugin-utils": "^4.12.1", + "gatsby-core-utils": "^4.12.1", + "gatsby-plugin-utils": "^4.12.3", "lodash": "^4.17.21", "probe-image-size": "^7.2.3", "semver": "^7.5.3", @@ -11900,9 +11900,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/gatsby-plugin-typescript": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/gatsby-plugin-typescript/-/gatsby-plugin-typescript-5.12.0.tgz", - "integrity": "sha512-6oLxghN1y/XqDQKg8MwWgvJnkQu+5D+5NZqOlpDsGkJQz+k06S3WTK+diGnGJ9epmE0i7vCY5ZutGgrJ7icA+w==", + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/gatsby-plugin-typescript/-/gatsby-plugin-typescript-5.12.1.tgz", + "integrity": "sha512-NIigc9TnhjLam/WAQxvVLKpRgjOXzDDgetOt2F2qtO+1KjMuUgLxHdd613Z0JoSPGpi5ug0KG8U99gh9zge7jA==", "dependencies": { "@babel/core": "^7.20.12", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", @@ -11910,7 +11910,7 @@ "@babel/plugin-proposal-optional-chaining": "^7.20.7", "@babel/preset-typescript": "^7.18.6", "@babel/runtime": "^7.20.13", - "babel-plugin-remove-graphql-queries": "^5.12.0" + "babel-plugin-remove-graphql-queries": "^5.12.1" }, "engines": { "node": ">=18.0.0" @@ -11920,14 +11920,14 @@ } }, "node_modules/gatsby-plugin-utils": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/gatsby-plugin-utils/-/gatsby-plugin-utils-4.12.1.tgz", - "integrity": "sha512-WL8x0E4CkmP1m61aaIa23lhoOOpxH7PIlZCzly2ehcsZmyL/7dxfpm09B/IAkLYbSMzi6Uln1vmQAN7qvBan4A==", + "version": "4.12.3", + "resolved": "https://registry.npmjs.org/gatsby-plugin-utils/-/gatsby-plugin-utils-4.12.3.tgz", + "integrity": "sha512-AMagRfVAIwc3w66RZzq9cGPma3pkrGe/iyhktmHWDOtu45tOt0zlbSY91juuCw2Oov17WzJp2TWKQ/i0nkuLbA==", "dependencies": { "@babel/runtime": "^7.20.13", "fastq": "^1.15.0", "fs-extra": "^11.1.1", - "gatsby-core-utils": "^4.12.0", + "gatsby-core-utils": "^4.12.1", "gatsby-sharp": "^1.12.1", "graphql-compose": "^9.0.10", "import-from": "^4.0.0", @@ -11984,15 +11984,15 @@ } }, "node_modules/gatsby-source-filesystem": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/gatsby-source-filesystem/-/gatsby-source-filesystem-5.12.0.tgz", - "integrity": "sha512-0BZkgADBu56vTzZ6TLvrMXhp8MzpEqvtpsI+VRtNTlsu6ULaHRjoMClomlAqWecjBXTkujhwpSjnIfEmpmCaLQ==", + "version": "5.12.1", + "resolved": "https://registry.npmjs.org/gatsby-source-filesystem/-/gatsby-source-filesystem-5.12.1.tgz", + "integrity": "sha512-JV6lttId7cBDasVaweFbKuDQP9VH8fFgI1Mg0sM/tfydfKTLXiEbBadKgEHbvbwAZsYjK+/KvT7A58dzrzDdXw==", "dependencies": { "@babel/runtime": "^7.20.13", "chokidar": "^3.5.3", "file-type": "^16.5.4", "fs-extra": "^11.1.1", - "gatsby-core-utils": "^4.12.0", + "gatsby-core-utils": "^4.12.1", "mime": "^3.0.0", "pretty-bytes": "^5.6.0", "valid-url": "^1.0.9", @@ -12006,9 +12006,9 @@ } }, "node_modules/gatsby-telemetry": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/gatsby-telemetry/-/gatsby-telemetry-4.12.0.tgz", - "integrity": "sha512-pgaGCzKPZKWvNrX/VC/nE1S9Z20fzg4aA4ETD6hlI7ztu+BSyQG+Oebly4SdFGlVSLeq3x3+gOe/LY9Fry7TrA==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/gatsby-telemetry/-/gatsby-telemetry-4.12.1.tgz", + "integrity": "sha512-MTHcKt5Cl68DveBpsduwfJdRjoXg48fcjITo1TspbxS2R0WnTZPRohGbA+JmQdY+O1eUSysdrONIjf6r86nhiA==", "hasInstallScript": true, "dependencies": { "@babel/code-frame": "^7.18.6", @@ -12018,7 +12018,7 @@ "boxen": "^5.1.2", "configstore": "^5.0.1", "fs-extra": "^11.1.1", - "gatsby-core-utils": "^4.12.0", + "gatsby-core-utils": "^4.12.1", "git-up": "^7.0.0", "is-docker": "^2.2.1", "lodash": "^4.17.21", @@ -12029,15 +12029,15 @@ } }, "node_modules/gatsby-transformer-sharp": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/gatsby-transformer-sharp/-/gatsby-transformer-sharp-5.12.1.tgz", - "integrity": "sha512-xcaWxQgdp8990Tyn88EyGQNl382trLumjYBtzL83blnQIJmbKhUoQDp4c/auZ1HylUw0IKJFvma2Yu6Ri3C7Bw==", + "version": "5.12.3", + "resolved": "https://registry.npmjs.org/gatsby-transformer-sharp/-/gatsby-transformer-sharp-5.12.3.tgz", + "integrity": "sha512-unVF6XJX2W6XU1NzqsSPAHQJQa8L3XIFROqXsvlLvPNgYDyXdczV33XZGNg8XaLIbPWNo9eHwOPxzS5TvBz4Fg==", "dependencies": { "@babel/runtime": "^7.20.13", "bluebird": "^3.7.2", "common-tags": "^1.8.2", "fs-extra": "^11.1.1", - "gatsby-plugin-utils": "^4.12.1", + "gatsby-plugin-utils": "^4.12.3", "probe-image-size": "^7.2.3", "semver": "^7.5.3", "sharp": "^0.32.6" @@ -14510,27 +14510,27 @@ "integrity": "sha512-kmsGcmpvjStZ0ATjuHycBujtNnXiZR28BTivEu0gAMDTT7GEyodcK6zSRtu6xsrdorrPZEIN380x7BD7xEYkew==" }, "node_modules/lint-staged": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-14.0.1.tgz", - "integrity": "sha512-Mw0cL6HXnHN1ag0mN/Dg4g6sr8uf8sn98w2Oc1ECtFto9tvRF7nkXGJRbx8gPlHyoR0pLyBr2lQHbWwmUHe1Sw==", + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.0.2.tgz", + "integrity": "sha512-vnEy7pFTHyVuDmCAIFKR5QDO8XLVlPFQQyujQ/STOxe40ICWqJ6knS2wSJ/ffX/Lw0rz83luRDh+ET7toN+rOw==", "dev": true, "dependencies": { "chalk": "5.3.0", - "commander": "11.0.0", + "commander": "11.1.0", "debug": "4.3.4", - "execa": "7.2.0", + "execa": "8.0.1", "lilconfig": "2.1.0", - "listr2": "6.6.1", + "listr2": "7.0.2", "micromatch": "4.0.5", "pidtree": "0.6.0", "string-argv": "0.3.2", - "yaml": "2.3.1" + "yaml": "2.3.3" }, "bin": { "lint-staged": "bin/lint-staged.js" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=18.12.0" }, "funding": { "url": "https://opencollective.com/lint-staged" @@ -14619,56 +14619,56 @@ } }, "node_modules/lint-staged/node_modules/commander": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", - "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true, "engines": { "node": ">=16" } }, "node_modules/lint-staged/node_modules/execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", - "signal-exit": "^3.0.7", + "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" }, "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + "node": ">=16.17" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, "node_modules/lint-staged/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, "engines": { - "node": ">=10" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lint-staged/node_modules/human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, "engines": { - "node": ">=14.18.0" + "node": ">=16.17.0" } }, "node_modules/lint-staged/node_modules/is-fullwidth-code-point": { @@ -14696,9 +14696,9 @@ } }, "node_modules/lint-staged/node_modules/listr2": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz", - "integrity": "sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-7.0.2.tgz", + "integrity": "sha512-rJysbR9GKIalhTbVL2tYbF2hVyDnrf7pFUZBwjPaMIdadYHmeT+EVi/Bu3qd7ETQPahTotg2WRCatXwRBW554g==", "dev": true, "dependencies": { "cli-truncate": "^3.1.0", @@ -14710,14 +14710,6 @@ }, "engines": { "node": ">=16.0.0" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" - }, - "peerDependenciesMeta": { - "enquirer": { - "optional": true - } } }, "node_modules/lint-staged/node_modules/log-update": { @@ -14833,6 +14825,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lint-staged/node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/lint-staged/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/lint-staged/node_modules/slice-ansi": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", @@ -14923,9 +14933,9 @@ } }, "node_modules/lint-staged/node_modules/yaml": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", - "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.3.tgz", + "integrity": "sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==", "dev": true, "engines": { "node": ">= 14" @@ -15661,9 +15671,9 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" }, "node_modules/n3": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/n3/-/n3-1.17.1.tgz", - "integrity": "sha512-HlanMWpvN2kcTrFuU3GPObyY7qrVQWy2Hp7l4GSXJlcQapjQMR7OM4kCr788pTQzNIpiHS3JRvyZ2YUcYJ82rA==", + "version": "1.17.2", + "resolved": "https://registry.npmjs.org/n3/-/n3-1.17.2.tgz", + "integrity": "sha512-BxSM52wYFqXrbQQT5WUEzKUn6qpYV+2L4XZLfn3Gblz2kwZ09S+QxC33WNdVEQy2djenFL8SNkrjejEKlvI6+Q==", "dependencies": { "queue-microtask": "^1.1.2", "readable-stream": "^4.0.0" @@ -19317,9 +19327,9 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "node_modules/sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", "dev": true, "dependencies": { "asn1": "~0.2.3", @@ -19367,9 +19377,9 @@ "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" }, "node_modules/start-server-and-test": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.1.tgz", - "integrity": "sha512-8PFo4DLLLCDMuS51/BEEtE1m9CAXw1LNVtZSS1PzkYQh6Qf9JUwM4huYeSoUumaaoAyuwYBwCa9OsrcpMqcOdQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/start-server-and-test/-/start-server-and-test-2.0.2.tgz", + "integrity": "sha512-4sGS2QmETUwqeBUqtTLP7OqXp3PdDnevaWlPlrFQgn8+7uCgVg4Do7/H/ZhAAVyvnL3DqKyANhnLgcgxrjhrMA==", "dev": true, "dependencies": { "arg": "^5.0.2", @@ -19379,7 +19389,7 @@ "execa": "5.1.1", "lazy-ass": "1.6.0", "ps-tree": "1.2.0", - "wait-on": "7.0.1" + "wait-on": "7.1.0" }, "bin": { "server-test": "src/bin/start.js", @@ -20508,6 +20518,11 @@ "node": ">=14.0" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -20953,16 +20968,16 @@ } }, "node_modules/wait-on": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.0.1.tgz", - "integrity": "sha512-9AnJE9qTjRQOlTZIldAaf/da2eW0eSRSgcqq85mXQja/DW3MriHxkpODDSUEg+Gri/rKEcXUZHe+cevvYItaog==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.1.0.tgz", + "integrity": "sha512-U7TF/OYYzAg+OoiT/B8opvN48UHt0QYMi4aD3PjRFpybQ+o6czQF8Ig3SKCCMJdxpBrCalIJ4O00FBof27Fu9Q==", "dev": true, "dependencies": { "axios": "^0.27.2", - "joi": "^17.7.0", + "joi": "^17.11.0", "lodash": "^4.17.21", - "minimist": "^1.2.7", - "rxjs": "^7.8.0" + "minimist": "^1.2.8", + "rxjs": "^7.8.1" }, "bin": { "wait-on": "bin/wait-on" diff --git a/package.json b/package.json index 7f64a695..a218f934 100644 --- a/package.json +++ b/package.json @@ -36,13 +36,13 @@ }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", - "@testing-library/cypress": "^9.0.0", + "@testing-library/cypress": "^10.0.1", "@testing-library/jest-dom": "^6.0.0", "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.4.3", "@vitejs/plugin-react": "^4.0.4", "@vitest/coverage-v8": "^0.34.2", - "cypress": "^12.3.0", + "cypress": "^13.4.0", "eslint": "^8.29.0", "eslint-config-react-app": "^7.0.0", "eslint-plugin-cypress": "^2.12.1", @@ -50,7 +50,7 @@ "husky": "^8.0.2", "identity-obj-proxy": "^3.0.0", "jsdom": "^22.1.0", - "lint-staged": "^14.0.0", + "lint-staged": "^15.0.2", "prettier": "^3.0.2", "start-server-and-test": "^2.0.0", "vitest": "^0.34.1" @@ -73,8 +73,8 @@ "prepare": "husky install", "cy:open": "cypress open", "cy:run": "cypress run", - "test:e2e": "cypress/prepare-cypress-test.sh && start-server-and-test develop http://localhost:8000/index.de.html cy:open", - "test:e2e:ci": "cypress/prepare-cypress-test.sh && start-server-and-test develop http://localhost:8000/index.de.html cy:run" + "test:e2e": "cypress/prepare-cypress-test.sh && start-server-and-test develop http://0.0.0.0:8000 cy:open", + "test:e2e:ci": "cypress/prepare-cypress-test.sh && start-server-and-test develop http://0.0.0.0:8000 cy:run" }, "repository": { "type": "git", diff --git a/src/common.js b/src/common.js index 03e91822..54c0efd7 100644 --- a/src/common.js +++ b/src/common.js @@ -141,6 +141,7 @@ function loadConfig(configFile, defaultFile) { searchableAttributes: userConfig.searchableAttributes || defaults.searchableAttributes, customDomain: userConfig.custom_domain || "", + failOnValidation: userConfig.fail_on_validation, } // check if all relevant colors are contained, otherwise use default colors @@ -193,6 +194,26 @@ function loadConfig(configFile, defaultFile) { return config } +/** + * Location object from reach router, see https://www.gatsbyjs.com/docs/location-data-from-props/ + * @typedef {Object} Location + * @property {string} key + * @property {string} pathname + * @property {string} search + */ + +/** + * Parses the location object for an URL parameter "lang". + * If multiple "lang" params are given, the first one is taken. + * @param {Location} location + * @returns {string|null} parsed language or null if none is given + */ +const getLanguageFromUrl = (location) => { + const params = new URLSearchParams(location.search) + const language = params.get("lang") + return language +} + module.exports = { i18n, getFilePath, @@ -202,4 +223,5 @@ module.exports = { getLinkPath, parseLanguages, loadConfig, + getLanguageFromUrl, } diff --git a/src/components/Collection.jsx b/src/components/Collection.jsx index c3bdca5e..4dcd86b9 100644 --- a/src/components/Collection.jsx +++ b/src/components/Collection.jsx @@ -1,25 +1,36 @@ import { Link } from "gatsby" import { i18n, getFilePath } from "../common" import JsonLink from "./JsonLink" +import { useSkoHubContext } from "../context/Context" +import { useEffect, useState } from "react" -const Collection = ({ - pageContext: { node: collection, language, customDomain }, -}) => ( -
-

{i18n(language)(collection.prefLabel)}

-

{collection.id}

- - -
-) +const Collection = ({ pageContext: { node: collection, customDomain } }) => { + const { data } = useSkoHubContext() + const [language, setLanguage] = useState("") + + useEffect(() => { + if (data.selectedLanguage !== "") { + setLanguage(data.selectedLanguage) + } + }, [data?.selectedLanguage]) + + return ( +
+

{i18n(language)(collection.prefLabel)}

+

{collection.id}

+ + +
+ ) +} export default Collection diff --git a/src/components/Concept.jsx b/src/components/Concept.jsx index bc858b07..4d3aaf99 100644 --- a/src/components/Concept.jsx +++ b/src/components/Concept.jsx @@ -2,13 +2,21 @@ import Markdown from "markdown-to-jsx" import { Link } from "gatsby" import JsonLink from "./JsonLink.jsx" import { getConceptSchemes } from "../hooks/getConceptSchemes" +import { useSkoHubContext } from "../context/Context.jsx" import { i18n, getDomId, getFilePath } from "../common" import ConceptURI from "./ConceptURI.jsx" +import { useEffect, useState } from "react" const Concept = ({ - pageContext: { node: concept, language, collections, customDomain }, + pageContext: { node: concept, collections, customDomain }, }) => { const conceptSchemes = getConceptSchemes() + const { data } = useSkoHubContext() + const [language, setLanguage] = useState("") + + useEffect(() => { + setLanguage(data.selectedLanguage) + }, [data?.selectedLanguage]) return (
@@ -166,13 +174,7 @@ const Concept = ({
) } diff --git a/src/components/nestedList.jsx b/src/components/nestedList.jsx index daf8e9ea..058ed568 100644 --- a/src/components/nestedList.jsx +++ b/src/components/nestedList.jsx @@ -1,9 +1,10 @@ -import React from "react" +import React, { useEffect, useState } from "react" import { css } from "@emotion/react" import { i18n, getFilePath, getFragment } from "../common" import { Link as GatsbyLink } from "gatsby" import { getConfigAndConceptSchemes } from "../hooks/configAndConceptSchemes" +import { useSkoHubContext } from "../context/Context" const getNestedItems = (item) => { let ids = [item.id] @@ -36,6 +37,7 @@ const NestedList = ({ customDomain, }) => { const { config } = getConfigAndConceptSchemes() + const style = css` list-style-type: none; padding: 0; @@ -123,6 +125,13 @@ const NestedList = ({ } } ` + const { data, _ } = useSkoHubContext() + useEffect(() => { + if (!language) { + language = data.selectedLanguage + } + }, [data?.selectedLanguage]) + const filteredIds = queryFilter && queryFilter.length ? queryFilter.flatMap((f) => f.result) @@ -267,7 +276,7 @@ const NestedList = ({ "aria-current": item.id === current ? "true" : "false", ...(LinkTag === "a" ? { href: getFragment(item.id) } - : { to: getFilePath(item.id, `${language}.html`, customDomain) }), + : { to: getFilePath(item.id, `html`, customDomain) }), }, children ) diff --git a/src/context/Context.jsx b/src/context/Context.jsx index ae6783ac..730b5510 100644 --- a/src/context/Context.jsx +++ b/src/context/Context.jsx @@ -2,6 +2,9 @@ import React, { createContext, useContext, useState } from "react" const defaultState = { currentScheme: {}, + selectedLanguage: "", + conceptSchemeLanguages: [], + indexPage: false, } const Context = createContext(defaultState) diff --git a/src/hooks/configAndConceptSchemes.js b/src/hooks/configAndConceptSchemes.js index ff31cd85..2becd677 100644 --- a/src/hooks/configAndConceptSchemes.js +++ b/src/hooks/configAndConceptSchemes.js @@ -36,6 +36,7 @@ export const getConfigAndConceptSchemes = () => { } searchableAttributes customDomain + failOnValidation } } allConceptScheme { diff --git a/src/hooks/getConceptSchemes.js b/src/hooks/getConceptSchemes.js index d50aebf5..2f015875 100644 --- a/src/hooks/getConceptSchemes.js +++ b/src/hooks/getConceptSchemes.js @@ -1,5 +1,14 @@ import { useStaticQuery, graphql } from "gatsby" +/** + * @typedef {Object} ConceptSchemes + * @property {object} nodeId - An object with the languages field as the value. + * @property {string[]} nodeId.languages - An array of strings representing the languages associated with the node. + */ +/** + * Maps over an array of edges and returns an object with the id of each node as the key and an object with the languages field as the value. + * @returns {ConceptSchemes} An object with the id of each node as the key and an object with the languages field as the value. + */ export const getConceptSchemes = () => { const { allConceptScheme } = useStaticQuery(graphql` query ConceptSchemes { @@ -15,6 +24,8 @@ export const getConceptSchemes = () => { } } `) + + /** @type {ConceptSchemes} */ const conceptSchemes = allConceptScheme.edges .map(({ node }) => ({ [node.id]: { languages: node.fields.languages }, diff --git a/src/hooks/getUserLanguage.js b/src/hooks/getUserLanguage.js new file mode 100644 index 00000000..5c7c2760 --- /dev/null +++ b/src/hooks/getUserLanguage.js @@ -0,0 +1,23 @@ +/** + * @param {object} concept + * @param {object} conceptSchemes + */ +export const getUserLang = ({ availableLanguages = [], selectedLanguage }) => { + if (typeof window !== "undefined") { + /** @prop {string} */ + const userLang = (navigator.language || navigator.userLanguage).slice(0, 2) + if (selectedLanguage && availableLanguages.includes(selectedLanguage)) { + return selectedLanguage + } else if ( + selectedLanguage && + !availableLanguages.includes(selectedLanguage) && + availableLanguages.includes(userLang) + ) { + return userLang + } else if (availableLanguages.includes(userLang)) return userLang + else { + const language = availableLanguages[0] + return language + } + } +} diff --git a/src/pages/index.js b/src/pages/index.js new file mode 100644 index 00000000..f659a6ba --- /dev/null +++ b/src/pages/index.js @@ -0,0 +1,93 @@ +import React, { useEffect, useState } from "react" +import { Link } from "gatsby" +import { i18n, getFilePath, getLanguageFromUrl } from "../common" +import { useSkoHubContext } from "../context/Context" +import { getUserLang } from "../hooks/getUserLanguage" +import { getConfigAndConceptSchemes } from "../hooks/configAndConceptSchemes.js" + +import Layout from "../components/layout" +import SEO from "../components/seo" + +const IndexPage = ({ location }) => { + const [conceptSchemes, setConceptSchemes] = useState([]) + const [language, setLanguage] = useState("") + const { data, updateState } = useSkoHubContext() + const { config } = getConfigAndConceptSchemes() + const customDomain = config.customDomain + + useEffect(() => { + async function fetchConceptData() { + const res = await fetch("index.json") + const csData = await res.json() + setConceptSchemes(csData) + const languages = Array.from( + new Set([...csData.flatMap((cs) => cs.languages)]) + ) + updateState({ ...data, languages: languages, indexPage: true }) + } + fetchConceptData() + }, []) + + // set language stuff + useEffect(() => { + const languageFromUrl = getLanguageFromUrl(location) + if (languageFromUrl && !data.selectedLanguage) { + const userLang = getUserLang({ + availableLanguages: data.languages, + selectedLanguage: languageFromUrl, + }) + + setLanguage(userLang) + updateState({ + ...data, + selectedLanguage: userLang, + indexPage: true, + currentScheme: {}, + }) + } else { + const userLang = getUserLang({ + availableLanguages: data.languages, + selectedLanguage: data?.selectedLanguage || null, + }) + setLanguage(userLang) + updateState({ + ...data, + selectedLanguage: userLang, + indexPage: true, + currentScheme: {}, + }) + } + }, [data?.languages, data?.selectedLanguage]) + + return ( + + +
+
    + {conceptSchemes.map((conceptScheme) => ( +
  • + + updateState({ + ...data, + conceptSchemeLanguages: [...conceptScheme.languages], + currentScheme: conceptScheme, + selectedLanguage: conceptScheme.languages.includes(language) + ? language + : conceptScheme.languages[0], + }) + } + to={getFilePath(conceptScheme.id, `html`, customDomain)} + > + {(conceptScheme.title && i18n(language)(conceptScheme.title)) || + conceptScheme.id} + +
  • + ))} +
+
+
+ ) +} + +export default IndexPage diff --git a/src/styles/concepts.css.js b/src/styles/concepts.css.js index acfc24c4..4e0f496e 100644 --- a/src/styles/concepts.css.js +++ b/src/styles/concepts.css.js @@ -72,14 +72,6 @@ export const conceptStyle = (colors) => css` } } - .concept .content:not(:target) { - display: none; - } - - .concept .content:target ~ div { - display: none; - } - .content { position: relative; overflow: auto; diff --git a/src/templates/App.jsx b/src/templates/App.jsx index 72960da0..57cd894b 100644 --- a/src/templates/App.jsx +++ b/src/templates/App.jsx @@ -1,6 +1,11 @@ import React, { useEffect, useState } from "react" import escapeRegExp from "lodash.escaperegexp" -import { i18n, getFilePath } from "../common" +import { + i18n, + getFilePath, + getLanguageFromUrl, + replaceFilePathInUrl, +} from "../common" import NestedList from "../components/nestedList" import TreeControls from "../components/TreeControls" import Layout from "../components/layout" @@ -9,13 +14,14 @@ import Search from "../components/Search" import { conceptStyle } from "../styles/concepts.css.js" import { getConfigAndConceptSchemes } from "../hooks/configAndConceptSchemes" +import { getUserLang } from "../hooks/getUserLanguage" import { useSkoHubContext } from "../context/Context.jsx" import { withPrefix } from "gatsby" import { handleKeypresses, importIndex } from "./helpers" -const App = ({ pageContext, children }) => { - const { data } = useSkoHubContext() - const { config } = getConfigAndConceptSchemes() +const App = ({ pageContext, children, location }) => { + const { data, updateState } = useSkoHubContext() + const { config, conceptSchemes } = getConfigAndConceptSchemes() const style = conceptStyle(config.colors) const [index, setIndex] = useState({}) const [query, setQuery] = useState(null) @@ -44,16 +50,89 @@ const App = ({ pageContext, children }) => { } } + const [language, setLanguage] = useState("") + const [currentScheme, setCurrentScheme] = useState(null) + + // get current scheme + useEffect(() => { + const fetchConceptSchemeForCollection = async (collection) => { + for (const member of collection.member) { + const path = replaceFilePathInUrl( + location.pathname, + member.id, + "json", + config.customDomain + ) + const res = await (await fetch(path)).json() + const cs = res.inScheme[0] + if (res.type === "Concept") { + return cs + } + } + } + + const getCurrentScheme = async () => { + if (pageContext.node.type === "ConceptScheme") + setCurrentScheme(pageContext.node) + else if (pageContext.node.type === "Concept") + setCurrentScheme(pageContext.node.inScheme[0]) + else if (pageContext.node.type === "Collection") { + const cs = await fetchConceptSchemeForCollection(pageContext.node) + setCurrentScheme(cs) + } else return {} + } + getCurrentScheme() + }, []) + + // set language stuff + useEffect(() => { + if (currentScheme) { + const languageFromUrl = getLanguageFromUrl(location) + + if (languageFromUrl && !data.selectedLanguage) { + const userLang = getUserLang({ + availableLanguages: conceptSchemes[currentScheme.id].languages, + selectedLanguage: languageFromUrl, + }) + setLanguage(userLang) + updateState({ + ...data, + currentScheme, + indexPage: false, + selectedLanguage: userLang, + availableLanguages: conceptSchemes[currentScheme.id].languages, + }) + } else { + const userLang = getUserLang({ + availableLanguages: conceptSchemes[currentScheme.id].languages, + selectedLanguage: data?.selectedLanguage || null, + }) + setLanguage(userLang) + updateState({ + ...data, + currentScheme, + indexPage: false, + selectedLanguage: userLang, + availableLanguages: conceptSchemes[currentScheme.id].languages, + }) + } + } + }, [data?.languages, data?.selectedLanguage, currentScheme]) + + // useEffect(() => { + // data?.conceptSchemeLanguages && setLanguage(data.selectedLanguage) + // }, [data?.selectedLanguage]) + // Fetch and load the serialized index useEffect(() => { importIndex( data?.currentScheme?.id, labels, - pageContext.language, + data.selectedLanguage, setIndex, config.customDomain ) - }, [data, pageContext.language, labels]) + }, [data, language, labels]) // Fetch and load the tree useEffect(() => { @@ -83,7 +162,7 @@ const App = ({ pageContext, children }) => { const toggleClick = (e) => setLabels({ ...labels, [e]: !labels[e] }) return ( - + { query && index?.search ? index.search(query) : null } highlight={query ? RegExp(escapeRegExp(query), "gi") : null} - language={pageContext.language} + language={language} topLevel={true} customDomain={config.customDomain} /> diff --git a/src/templates/helpers.js b/src/templates/helpers.js index 131d6b80..fafe5ecc 100644 --- a/src/templates/helpers.js +++ b/src/templates/helpers.js @@ -96,10 +96,7 @@ export const importIndex = async ( let data try { const path = - (conceptSchemeId.endsWith("/") - ? conceptSchemeId.slice(0, -1) - : conceptSchemeId) + `-cs/search/${language}/${key}` - + getFilePath(conceptSchemeId) + `-cs/search/${language}/${key}` data = await fetch(withPrefix(getFilePath(path, `json`, customDomain))) const jsonData = await data.json() idx.import(key, jsonData ?? null) diff --git a/test/App.test.jsx b/test/App.test.jsx index 719c6dea..d640b4f9 100644 --- a/test/App.test.jsx +++ b/test/App.test.jsx @@ -12,7 +12,15 @@ import { ConceptPC, ConceptSchemePC, CollectionPC } from "./data/pageContext" import mockFetch from "./mocks/mockFetch" import { mockConfig } from "./mocks/mockConfig" import userEvent from "@testing-library/user-event" -import { ContextProvider } from "../src/context/Context" +import { ContextProvider, useSkoHubContext } from "../src/context/Context" + +vi.mock("../src/context/Context.jsx", async () => { + const actual = await vi.importActual("../src/context/Context.jsx") + return { + ...actual, + useSkoHubContext: vi.fn(), + } +}) vi.mock("flexsearch/dist/module/document.js", async () => { const { Document } = await vi.importActual("flexsearch") @@ -22,11 +30,15 @@ vi.mock("flexsearch/dist/module/document.js", async () => { }) const useStaticQuery = vi.spyOn(Gatsby, `useStaticQuery`) -function renderApp(history, pageContext, children = null) { +function renderApp(history, pageContext, location, children = null) { return render( - + ) @@ -41,19 +53,34 @@ describe("App", () => { }) useStaticQuery.mockImplementation(() => mockConfig) + useSkoHubContext.mockReturnValue({ + data: { + conceptSchemeLanguages: ["de", "en"], + currentScheme: { + id: "http://w3id.org/", + title: { + de: "Test Vokabular", + }, + }, + selectedLanguage: "de", + }, + updateState: vi.fn(), + }) it("renders App component with expand and collapse button", async () => { - const route = "/w3id.org/index.de.html" + const route = "/w3id.org/index.html" const history = createHistory(createMemorySource(route)) + const location = { search: "?lang=de" } await act(() => { - renderApp(history, ConceptSchemePC) + renderApp(history, ConceptSchemePC, location) }) expect(screen.getByRole("button", { name: "Collapse" })).toBeInTheDocument() expect(screen.getByRole("button", { name: "Expand" })).toBeInTheDocument() }) it("renders App component **without** collapse and expand button", async () => { - const route = "/w3id.org/index.de.html" + const route = "/w3id.org/index.html" const history = createHistory(createMemorySource(route)) + const location = { search: "?lang=de" } // remove narrower from concept const topConcept = ConceptSchemePC.node.hasTopConcept[0] @@ -66,7 +93,7 @@ describe("App", () => { } await act(() => { - renderApp(history, pageContext) + renderApp(history, pageContext, location) }) expect(screen.queryByRole("button", { name: "Collapse" })).toBeNull() expect(screen.queryByRole("button", { name: "Expand" })).toBeNull() @@ -74,11 +101,12 @@ describe("App", () => { it("correctly fetches tree when page context is a concept", async () => { window.HTMLElement.prototype.scrollIntoView = function () {} - const route = "/w3id.org/c1.de.html" + const route = "/w3id.org/c1.html" const history = createHistory(createMemorySource(route)) + const location = { search: "?lang=de" } await act(() => { - renderApp(history, ConceptPC) + renderApp(history, ConceptPC, location) }) // we render the concept with notation therefore the "1" expect( @@ -89,11 +117,12 @@ describe("App", () => { it("correctly fetches tree when page context is a collection", async () => { window.HTMLElement.prototype.scrollIntoView = function () {} - const route = "/w3id.org/collection.de.html" + const route = "/w3id.org/collection.html" const history = createHistory(createMemorySource(route)) + const location = { search: "", pathname: route } await act(() => { - renderApp(history, CollectionPC) + renderApp(history, CollectionPC, location) }) // we render the concept with notation therefore the "1" expect( @@ -105,10 +134,12 @@ describe("App", () => { it("search is working", async () => { const user = userEvent.setup() - const route = "/w3id.org/index.de.html" + const route = "/w3id.org/index.html" const history = createHistory(createMemorySource(route)) + const location = { search: "?lang=de" } + await act(() => { - renderApp(history, ConceptSchemePC) + renderApp(history, ConceptSchemePC, location) }) expect(screen.queryByText("Konzept 1")).toBeInTheDocument() expect(screen.queryByText("Konzept 2")).toBeInTheDocument() diff --git a/test/collection.test.jsx b/test/collection.test.jsx index 4cc62d0e..b62e46f2 100644 --- a/test/collection.test.jsx +++ b/test/collection.test.jsx @@ -1,11 +1,53 @@ -import { describe, expect, it } from "vitest" +import { afterEach, describe, expect, it, vi } from "vitest" import { render, screen } from "@testing-library/react" import Collection from "../src/components/Collection" import { CollectionPC } from "./data/pageContext" +import { useSkoHubContext, ContextProvider } from "../src/context/Context.jsx" +import { + createHistory, + createMemorySource, + LocationProvider, +} from "@gatsbyjs/reach-router" +import * as Gatsby from "gatsby" +import { mockConfig } from "./mocks/mockConfig.js" +import mockFetch from "./mocks/mockFetch" + +const useStaticQuery = vi.spyOn(Gatsby, `useStaticQuery`) + +function renderCollection(history, pageContext) { + return render( + + + + + + ) +} describe("Collection", () => { + afterEach(() => { + vi.clearAllMocks() + }) + vi.mock("../src/context/Context.jsx", async () => { + const actual = await vi.importActual("../src/context/Context.jsx") + return { + ...actual, + useSkoHubContext: vi.fn(), + } + }) + useStaticQuery.mockImplementation(() => mockConfig) + const route = "/w3id.org/index.html" + const history = createHistory(createMemorySource(route)) + vi.spyOn(window, "fetch").mockImplementation(mockFetch) + it("renders collection component", () => { - render() + useSkoHubContext.mockReturnValue({ + data: { + selectedLanguage: "de", + }, + updateState: vi.fn(), + }) + renderCollection(history, CollectionPC) expect(screen.getByRole("link", { name: "Konzept 1" })).toBeInTheDocument() expect( screen.getByRole("link", { name: "Test Mitglied 2" }) @@ -13,16 +55,18 @@ describe("Collection", () => { }) it("shows no prefLabel-Message if none is provided", () => { - render( - - ) + useSkoHubContext.mockReturnValue({ + data: { + selectedLanguage: "en", + }, + updateState: vi.fn(), + }) + renderCollection(history, CollectionPC) expect(screen.getByRole("link", { name: /no label in language/i })) }) it("json link is working", () => { - render() + renderCollection(history, CollectionPC) expect(screen.getByRole("link", { name: "JSON" })).toHaveAttribute( "href", "/w3id.org/collection.json" diff --git a/test/common.test.js b/test/common.test.js index f1d7a19c..01ec4926 100644 --- a/test/common.test.js +++ b/test/common.test.js @@ -5,6 +5,7 @@ const { getFilePath, replaceFilePathInUrl, getLinkPath, + getLanguageFromUrl, } = require("../src/common") describe("Translate", () => { @@ -75,3 +76,18 @@ describe("getLinkPath", () => { ).toBe("../1.de.html") }) }) + +describe("getLanguageFromUrl", () => { + it("parses language if lang param in location.search is given", () => { + const location = { + search: "?lang=de", + } + expect(getLanguageFromUrl(location)).toBe("de") + }) + it("returns null, if no lang param is present in location.search", () => { + const location = { + search: "", + } + expect(getLanguageFromUrl(location)).toBeNull() + }) +}) diff --git a/test/concept.test.jsx b/test/concept.test.jsx index 4eac6c7a..a90d85e5 100644 --- a/test/concept.test.jsx +++ b/test/concept.test.jsx @@ -7,15 +7,33 @@ import Concept from "../src/components/Concept.jsx" import { ConceptPC } from "./data/pageContext" import mockFetch from "./mocks/mockFetch" import { mockConfig } from "./mocks/mockConfig" +import { useSkoHubContext } from "../src/context/Context.jsx" const useStaticQuery = vi.spyOn(Gatsby, `useStaticQuery`) describe.concurrent("Concept", () => { + afterEach(() => { + vi.clearAllMocks() + }) vi.spyOn(window, "fetch").mockImplementation(mockFetch) useStaticQuery.mockImplementation(() => mockConfig) + vi.mock("../src/context/Context.jsx", async () => { + const actual = await vi.importActual("../src/context/Context.jsx") + return { + ...actual, + useSkoHubContext: vi.fn(), + } + }) it("renders concept component", () => { - render() + useSkoHubContext.mockReturnValue({ + data: { + currentScheme: {}, + selectedLanguage: "de", + }, + updateState: vi.fn(), + }) + render() expect( screen.getByRole("heading", { name: /Konzept 1/i }) ).toBeInTheDocument() @@ -49,7 +67,21 @@ describe.concurrent("Concept", () => { }) it("renders no definition if not provided in language", () => { - render() + useSkoHubContext.mockReturnValue({ + data: { + currentScheme: {}, + selectedLanguage: "en", + }, + updateState: vi.fn(), + }) + render( + + ) expect(screen.queryByText("Meine Definition")).toBeNull() expect( screen.getByText('No definition in language "en" provided.') @@ -57,6 +89,13 @@ describe.concurrent("Concept", () => { }) it("renders altLabels", () => { + useSkoHubContext.mockReturnValue({ + data: { + currentScheme: {}, + selectedLanguage: "de", + }, + updateState: vi.fn(), + }) render() const list = screen.getByRole("list", { name: /alt label/i, @@ -181,7 +220,7 @@ describe.concurrent("Concept", () => { */ expect( screen.getByRole("link", { name: "http://w3id.org/cs2/" }) - ).toHaveAttribute("href", "/w3id.org/cs2/index.en.html") + ).toHaveAttribute("href", "/w3id.org/cs2/index.html") expect( screen.getByRole("link", { name: /just-another-scheme/i }) diff --git a/test/config.test.js b/test/config.test.js index 9b623bea..9ad2db69 100644 --- a/test/config.test.js +++ b/test/config.test.js @@ -26,6 +26,7 @@ describe("Config Parsing", () => { skoHubLightGrey: "rgb(235, 235, 235)", }, customDomain: "", + failOnValidation: true, fonts: { regular: { font_family: "Aladin", diff --git a/test/data/config/config.default.yaml b/test/data/config/config.default.yaml index 2075c943..f6a4601b 100644 --- a/test/data/config/config.default.yaml +++ b/test/data/config/config.default.yaml @@ -3,6 +3,7 @@ tokenizer: "full" # strict, forward, reverse, full searchableAttributes: - "prefLabel" +fail_on_validation: true ui: colors: skoHubWhite: "rgb(255, 255, 255)" diff --git a/test/data/config/config.test.yaml b/test/data/config/config.test.yaml index c4773b1c..aa8902cf 100644 --- a/test/data/config/config.test.yaml +++ b/test/data/config/config.test.yaml @@ -1,6 +1,5 @@ ---- -# see https://github.com/nextapps-de/flexsearch/tree/0.6.32#tokenizer for options tokenizer: "full" # strict, forward, reverse, full +fail_on_validation: true ui: title: "Hello World" logo: "my-logo.png" # Path @@ -26,4 +25,4 @@ ui: font_family: "Alegreya Sans" font_style: "normal" font_weight: 700 - name: "alegreya-sans-v24-latin-700" \ No newline at end of file + name: "alegreya-sans-v24-latin-700" diff --git a/test/header.test.jsx b/test/header.test.jsx index c27b7189..13ec8cb5 100644 --- a/test/header.test.jsx +++ b/test/header.test.jsx @@ -1,47 +1,46 @@ -import { describe, expect, it, vi } from "vitest" +import { afterEach, describe, expect, it, vi } from "vitest" import React from "react" import * as Gatsby from "gatsby" import { render, screen, act } from "@testing-library/react" import Header from "../src/components/header.jsx" import mockFetch from "./mocks/mockFetch" import { mockConfig } from "./mocks/mockConfig.js" - -import { - createHistory, - createMemorySource, - LocationProvider, -} from "@gatsbyjs/reach-router" -import { ContextProvider } from "../src/context/Context.jsx" +import { useSkoHubContext } from "../src/context/Context.jsx" const useStaticQuery = vi.spyOn(Gatsby, `useStaticQuery`) -function renderHeader(history, siteTitle, languages, language) { - return render( - - -
-
-
- ) -} - describe("Header", () => { + afterEach(() => { + vi.clearAllMocks() + }) vi.spyOn(window, "fetch").mockImplementation(mockFetch) useStaticQuery.mockImplementation(() => mockConfig) + vi.mock("../src/context/Context.jsx", async () => { + const actual = await vi.importActual("../src/context/Context.jsx") + return { + ...actual, + useSkoHubContext: vi.fn(), + } + }) const siteTitle = "Test Title" it("renders header component without language tags", async () => { - const languages = ["de"] - const language = "de" - const route = "/one-lang/w3id.org/index.de.html" - const history = createHistory(createMemorySource(route)) + useSkoHubContext.mockReturnValue({ + data: { + languages: ["de"], + currentScheme: { + id: "http://one-lang/w3id.org/", + title: { + de: "Test Vokabular", + }, + }, + selectedLanguage: "de", + }, + updateState: vi.fn(), + }) await act(() => { - renderHeader(history, siteTitle, languages, language) + render(
) }) expect(screen.getByRole("banner")).toBeInTheDocument() // skohub logo @@ -61,12 +60,21 @@ describe("Header", () => { }) it(`renders header component with link to concept scheme (slash URIs)`, async () => { - const languages = ["de", "en"] - const language = "de" - const route = "/w3id.org/index.de.html" - const history = createHistory(createMemorySource(route)) + useSkoHubContext.mockReturnValue({ + data: { + languages: ["de"], + currentScheme: { + id: "http://w3id.org/", + title: { + de: "Test Vokabular", + }, + }, + selectedLanguage: "de", + }, + updateState: vi.fn(), + }) await act(() => { - renderHeader(history, siteTitle, languages, language) + render(
) }) // skohub concept scheme link expect( @@ -77,12 +85,21 @@ describe("Header", () => { }) it(`renders header component with multiple language tags (slash URIs)`, async () => { - const languages = ["de", "en"] - const language = "de" - const route = "/w3id.org/index.de.html" - const history = createHistory(createMemorySource(route)) + useSkoHubContext.mockReturnValue({ + data: { + languages: ["de", "en"], + currentScheme: { + id: "http://w3id.org/", + title: { + de: "Test Vokabular", + }, + }, + selectedLanguage: "de", + }, + updateState: vi.fn(), + }) await act(() => { - renderHeader(history, siteTitle, languages, language) + render(
) }) // check for language menu expect(screen.getByRole("list")).toBeInTheDocument() @@ -93,13 +110,21 @@ describe("Header", () => { it("renders header component with multiple language tags (hash URIs)", async () => { // setting three languages here, but we only have two in the cs // so test should return only two + useSkoHubContext.mockReturnValue({ + data: { + currentScheme: { + id: "http://example.org/hashURIConceptScheme#scheme", + title: { + de: "Hash URI Konzept Schema", + }, + }, + selectedLanguage: "de", + }, + updateState: vi.fn(), + }) - const languages = ["de", "en", "uk"] - const language = "de" - const route = "/example.org/hashURIConceptScheme.de.html" - const history = createHistory(createMemorySource(route)) await act(() => { - renderHeader(history, siteTitle, languages, language) + render(
) }) // skohub concept scheme link expect( @@ -114,12 +139,17 @@ describe("Header", () => { }) it("renders header, shows concept id if title in language is not present", async () => { - const languages = ["de"] - const language = "en" - const route = "/no-title-in-en/w3id.org/index.de.html" - const history = createHistory(createMemorySource(route)) + useSkoHubContext.mockReturnValue({ + data: { + currentScheme: { + id: "http://w3id.org/", + }, + selectedLanguage: "de", + }, + updateState: vi.fn(), + }) await act(() => { - renderHeader(history, siteTitle, languages, language) + render(
) }) // skohub concept scheme link expect( @@ -131,12 +161,8 @@ describe("Header", () => { it("render component with concept data", async () => { // we reduce language array here artifically, because two languages should be found - const languages = ["de"] - const language = "de" - const route = "/w3id.org/c1.de.html" - const history = createHistory(createMemorySource(route)) await act(() => { - renderHeader(history, siteTitle, languages, language) + render(
) }) // check for language menu expect(screen.getByRole("list")).toBeInTheDocument() @@ -146,12 +172,8 @@ describe("Header", () => { it("render component with collection data", async () => { // we reduce language array here artifically, because two languages should be found - const languages = ["de"] - const language = "de" - const route = "/w3id.org/collection.de.html" - const history = createHistory(createMemorySource(route)) await act(() => { - renderHeader(history, siteTitle, languages, language) + render(
) }) // check for language menu expect(screen.getByRole("list")).toBeInTheDocument() @@ -162,23 +184,38 @@ describe("Header", () => { it("render languages if type is neither ConceptScheme, Concept or Collection", async () => { const languages = ["de", "en", "uk"] const language = "de" - const route = "/no-in-scheme/w3id.org/collection.de.html" - const history = createHistory(createMemorySource(route)) + useSkoHubContext.mockReturnValue({ + data: { + languages, + currentScheme: { + id: "http://no-in-scheme/w3id.org/", + title: { + de: "Test Vokabular", + }, + }, + selectedLanguage: language, + }, + updateState: vi.fn(), + }) await act(() => { - renderHeader(history, siteTitle, languages, language) + render(
) }) // check for language menu expect(screen.getByRole("list")).toBeInTheDocument() // check for language items expect(screen.getAllByRole("listitem").length).toBe(3) }) + it("render default languages if langs can't be received (e.g. if rendered on overview index", async () => { const languages = ["de", "en", "uk"] - const language = "de" - const route = "/no-valid-json/not-valid.de.html" - const history = createHistory(createMemorySource(route)) + useSkoHubContext.mockReturnValue({ + data: { + languages, + }, + updateState: vi.fn(), + }) await act(() => { - renderHeader(history, siteTitle, languages, language) + render(
) }) // check for language menu expect(screen.getByRole("list")).toBeInTheDocument() @@ -189,12 +226,21 @@ describe("Header", () => { it(`shows only one Concept Scheme link in header, if a concept is present in multiple concept schemes. Defaults to concept scheme id if no title in language provided.`, async () => { - const languages = ["de", "en"] - const language = "de" - const route = "/w3id.org/c1.de.html" - const history = createHistory(createMemorySource(route)) + useSkoHubContext.mockReturnValue({ + data: { + currentScheme: { + id: "http://w3id.org/", + title: { + de: "Test Vokabular", + }, + }, + selectedLanguage: "de", + }, + updateState: vi.fn(), + }) + await act(() => { - renderHeader(history, siteTitle, languages, language) + render(
) }) // skohub concept scheme link expect( diff --git a/test/mocks/mockConfig.js b/test/mocks/mockConfig.js index deb4be47..8e432748 100644 --- a/test/mocks/mockConfig.js +++ b/test/mocks/mockConfig.js @@ -68,6 +68,14 @@ export const mockConfig = { }, }, }, + { + node: { + id: "http://no-in-scheme/w3id.org/", + fields: { + languages: ["de", "en", "uk"], + }, + }, + }, ], }, } diff --git a/test/mocks/mockFetch.js b/test/mocks/mockFetch.js index 88aa13a4..4b5a0176 100644 --- a/test/mocks/mockFetch.js +++ b/test/mocks/mockFetch.js @@ -49,7 +49,7 @@ export default async function mockFetch(url) { const res = { ...ConceptScheme, title: { - ...ConceptScheme.title, + // ...ConceptScheme.title, en: null, }, } @@ -81,28 +81,28 @@ export default async function mockFetch(url) { json: async () => res, } } - case "/w3id.org-cs/search/de/prefLabel.cfg.json": { + case "/w3id.org/index-cs/search/de/prefLabel.cfg.json": { return { ok: true, status: 200, json: async () => cfg, } } - case "/w3id.org-cs/search/de/prefLabel.ctx.json": { + case "/w3id.org/index-cs/search/de/prefLabel.ctx.json": { return { ok: true, status: 200, json: async () => ctx, } } - case "/w3id.org-cs/search/de/prefLabel.map.json": { + case "/w3id.org/index-cs/search/de/prefLabel.map.json": { return { ok: true, status: 200, json: async () => map, } } - case "/w3id.org-cs/search/de/reg.json": { + case "/w3id.org/index-cs/search/de/reg.json": { return { ok: true, status: 200,