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}
-
-
- {collection.member.map((member) => (
-
-
- {i18n(language)(member.prefLabel) ||
- `*No label in language "${language}" provided.*`}
-
-
- ))}
-
-
-)
+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}
+
+
+ {collection.member.map((member) => (
+
+
+ {i18n(language)(member.prefLabel) ||
+ `*No label in language "${language}" provided.*`}
+
+
+ ))}
+
+
+ )
+}
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 = ({
{collections.map((collection) => (
-
+
{i18n(language)(collection.prefLabel) ||
`*No label in language "${language}" provided.*`}
@@ -192,17 +194,7 @@ const Concept = ({
otherwise link to first present language
*/}
{Object.keys(conceptSchemes).includes(inScheme.id) ? (
-
+
{inScheme.id}
) : (
diff --git a/src/components/ConceptScheme.jsx b/src/components/ConceptScheme.jsx
index 4df9bd39..801f4d3c 100644
--- a/src/components/ConceptScheme.jsx
+++ b/src/components/ConceptScheme.jsx
@@ -1,42 +1,53 @@
import Markdown from "markdown-to-jsx"
-
import Concept from "./Concept"
import { i18n, getDomId, getFilePath } from "../common"
import JsonLink from "./JsonLink"
import ConceptURI from "./ConceptURI"
+import { useSkoHubContext } from "../context/Context"
+import { useEffect, useState } from "react"
+import { useLocation } from "@gatsbyjs/reach-router"
const ConceptScheme = ({
- pageContext: { node: conceptScheme, embed, language, customDomain },
+ pageContext: { node: conceptScheme, embed, customDomain },
}) => {
- return (
-
- {
- /*
- we use embed here for embedding hashURI concepts
- */
- embed &&
- embed.map((concept) => (
-
- ))
- }
-
-
{i18n(language)(conceptScheme.title)}
-
-
- {conceptScheme.description && (
-
- {i18n(language)(conceptScheme.description)}
-
- )}
+ const { data } = useSkoHubContext()
+ const [language, setLanguage] = useState("")
+ useEffect(() => {
+ setLanguage(data.selectedLanguage)
+ }, [data?.selectedLanguage])
+
+ const pathname = useLocation()
+
+ // got some hash uri to show
+ if (pathname.hash) {
+ const filtered = embed.filter((c) => c.json.id.endsWith(pathname.hash))
+ return (
+
+
+
+ )
+ } else {
+ return (
+
+
+
{i18n(language)(conceptScheme.title)}
+
+
+ {conceptScheme.description && (
+
+ {i18n(language)(conceptScheme.description)}
+
+ )}
+
-
- )
+ )
+ }
}
export default ConceptScheme
diff --git a/src/components/header.jsx b/src/components/header.jsx
index 65558742..9f84a729 100644
--- a/src/components/header.jsx
+++ b/src/components/header.jsx
@@ -1,12 +1,12 @@
import React, { useEffect, useState } from "react"
import { css } from "@emotion/react"
-import { useLocation } from "@gatsbyjs/reach-router"
import { Link, withPrefix } from "gatsby"
-import { getFilePath, getLinkPath, replaceFilePathInUrl } from "../common"
+import { getFilePath } from "../common"
import { useSkoHubContext } from "../context/Context.jsx"
import { getConfigAndConceptSchemes } from "../hooks/configAndConceptSchemes"
+import { getUserLang } from "../hooks/getUserLanguage"
-const Header = ({ siteTitle, languages, language }) => {
+const Header = ({ siteTitle }) => {
const { config, conceptSchemes: conceptSchemesData } =
getConfigAndConceptSchemes()
const { data, updateState } = useSkoHubContext()
@@ -73,7 +73,8 @@ const Header = ({ siteTitle, languages, language }) => {
margin: 0 0 0 5px;
display: inline;
- a {
+ button {
+ background: none;
display: inline-block;
padding: 5px;
color: ${config.colors.skoHubMiddleGrey};
@@ -87,6 +88,7 @@ const Header = ({ siteTitle, languages, language }) => {
}
.currentLanguage {
+ color: black;
font-weight: bold;
display: inline-block;
padding: 5px;
@@ -96,69 +98,41 @@ const Header = ({ siteTitle, languages, language }) => {
}
}
`
+ const [languages, setLanguages] = useState([])
+ const [language, setLanguage] = useState("")
- const [langs, setLangs] = useState(new Set())
- const pathName = useLocation().pathname.slice(0, -8)
+ // set page language
+ useEffect(() => {
+ if (typeof languages !== "undefined" && languages.length) {
+ if (!data.selectedLanguage) {
+ const userLang = getUserLang({
+ availableLanguages: languages,
+ })
+ setLanguage(userLang)
+ // updateState({ ...data, selectedLanguage: userLang })
+ } else {
+ setLanguage(data.selectedLanguage)
+ }
+ }
+ }, [data])
- /**
- * To display the concept scheme title in the header
- * we have to retrieve concept scheme info in this component.
- * We do this by getting the path of the component and looking up
- * its information in the JSON data
- * */
+ // Set Languages
useEffect(() => {
- fetch(pathName + ".json")
- .then((response) => response.json())
- .then(async (r) => {
- if (r.type === "ConceptScheme") {
- updateState({ ...data, currentScheme: r })
- setLangs(() => new Set(conceptSchemesData[r.id].languages))
- } else if (r.type === "Concept") {
- // FIXME how to handle inScheme as array? Currently we fetch the first scheme
- // this could also be cached in local storage but that might also be a bit overkill
- const cs = r.inScheme[0]
- Object.keys(data.currentScheme).length === 0 &&
- updateState({ ...data, currentScheme: cs })
- setLangs(() => new Set(conceptSchemesData[cs.id].languages))
- } else if (r.type === "Collection") {
- // members of a collection can either be skos:Concepts or skos:Collection
- // so we need to check each member till we find a concept
- // from which we can derive the languages of the concept scheme
- for (const member of r.member) {
- const path = replaceFilePathInUrl(
- pathName,
- member.id,
- "json",
- config.customDomain
- )
- const res = await (await fetch(path)).json()
- const cs = res.inScheme[0]
- if (res.type === "Concept") {
- updateState({ ...data, currentScheme: cs })
- setLangs(() => new Set(conceptSchemesData[cs.id].languages))
- break
- }
- }
- } else {
- languages.forEach((l) => setLangs((prev) => new Set(prev.add(l))))
- }
- })
- .catch((err) => {
- /* FIXME Currently there is no general index.json
- * that we can use to retrieve languages when using header on the
- * index page so we need to set languages hard
- */
- if (typeof languages !== "undefined") {
- languages.forEach((l) => setLangs((prev) => new Set(prev.add(l))))
- }
- })
- }, [pathName, languages])
+ if (!data?.currentScheme?.id) {
+ setLanguages(data.languages)
+ } else {
+ setLanguages(conceptSchemesData[data.currentScheme.id].languages)
+ }
+ }, [data])
return (
-
+
updateState({ ...data, currentScheme: {} })}
+ >
{config.logo && (
{
- {data.currentScheme?.title?.[language] ||
+ {data.currentScheme?.title?.[data.selectedLanguage] ||
data.currentScheme.id}
)}
- {langs && Array.from(langs).length > 1 && (
+ {languages && languages.length > 1 && (
- {Array.from(langs).map((l) => (
+ {languages.map((l) => (
- {l === language ? (
- {l}
+ {l === data.selectedLanguage ? (
+ {l}
) : (
- {
+ updateState({ ...data, selectedLanguage: l })
+ setLanguage(l)
+ }}
>
{l}
-
+
)}
))}
diff --git a/src/components/index.jsx b/src/components/index.jsx
deleted file mode 100644
index 90bc942c..00000000
--- a/src/components/index.jsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import React from "react"
-import { Link } from "gatsby"
-import { i18n, getFilePath } from "../common"
-
-import Layout from "./layout"
-import SEO from "./seo"
-
-const IndexPage = ({
- pageContext: { conceptSchemes, language, languagesByCS, customDomain },
-}) => {
- const languages = Array.from(
- new Set([...Object.values(languagesByCS).flat()])
- )
- return (
-
-
-
-
- {conceptSchemes.map((conceptScheme) => (
-
-
- {i18n(language)(conceptScheme.title) || conceptScheme.id}
-
-
- ))}
-
-
-
- )
-}
-
-export default IndexPage
diff --git a/src/components/layout.jsx b/src/components/layout.jsx
index 296b6dcf..c5034966 100644
--- a/src/components/layout.jsx
+++ b/src/components/layout.jsx
@@ -14,7 +14,7 @@ import { getConfigAndConceptSchemes } from "../hooks/configAndConceptSchemes"
import Header from "./header.jsx"
import Footer from "./footer.jsx"
-const Layout = ({ children, languages, language }) => {
+const Layout = ({ children }) => {
const { config } = getConfigAndConceptSchemes()
const style = css`
height: 100vh;
@@ -51,7 +51,7 @@ const Layout = ({ children, languages, language }) => {
box-shadow: 0 10px 20px ${config.colors.skoHubBlackColor};
}
`
- const data = useStaticQuery(graphql`
+ const qdata = useStaticQuery(graphql`
query SiteTitleQuery {
site {
siteMetadata {
@@ -177,18 +177,10 @@ const Layout = ({ children, languages, language }) => {
}
`}
/>
-
+
{children}
-
+
)
}
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,