diff --git a/cypress/e2e/conceptSchemeAndConcept.cy.js b/cypress/e2e/conceptSchemeAndConcept.cy.js index 7689c06..9b385df 100644 --- a/cypress/e2e/conceptSchemeAndConcept.cy.js +++ b/cypress/e2e/conceptSchemeAndConcept.cy.js @@ -109,3 +109,25 @@ describe("Parsing language from URL on Concept Schemes and Concepts", () => { ) }) }) + +describe("DC properties for describing concept scheme are working", () => { + it("Title is present", () => { + cy.visit("/w3id.org/dc/index.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) + //header + cy.get(".conceptScheme > a").should("have.text", "Test Vokabular DC") + // concept content block + cy.get("h1").should("include.text", "Test Vokabular DC") + }) + it("Description is present", () => { + cy.visit("/w3id.org/dc/index.html", { + onBeforeLoad(win) { + Object.defineProperty(win.navigator, "language", { value: "de-DE" }) + }, + }) + cy.get(".markdown").should("have.text", "Test Beschreibung DC") + }) +}) diff --git a/cypress/e2e/index.cy.js b/cypress/e2e/index.cy.js index 74a7bbb..5fb44ab 100644 --- a/cypress/e2e/index.cy.js +++ b/cypress/e2e/index.cy.js @@ -6,7 +6,7 @@ describe("Main Vocab Index page", () => { }, }) // vocabs are found - cy.get(".centerPage > ul li").should("have.length", 7) + cy.get(".centerPage > ul li").should("have.length", 8) /** * What is tested by the existence of these links: @@ -35,6 +35,9 @@ describe("Main Vocab Index page", () => { cy.findByRole("link", { name: "Test Vokabular in zwei Dateien", }).should("exist") + cy.findByRole("link", { + name: "Test Vokabular DC", + }).should("exist") // switch language cy.get(".language-menu").contains("en").click() diff --git a/cypress/prepare-cypress-test.sh b/cypress/prepare-cypress-test.sh index 516838f..3451d7c 100755 --- a/cypress/prepare-cypress-test.sh +++ b/cypress/prepare-cypress-test.sh @@ -9,6 +9,7 @@ cp test/data/ttl/hashURIConceptScheme.ttl \ test/data/ttl/oneConceptSchemeTwoFiles_1.ttl \ test/data/ttl/oneConceptSchemeTwoFiles_2.ttl \ test/data/ttl/slashURIConceptScheme.ttl \ + test/data/ttl/slashURIConceptSchemeDCproperties.ttl \ test/data/ttl/systematik.ttl \ test/data/ttl/twoConceptSchemesOneFile.ttl \ data/ diff --git a/gatsby-node.js b/gatsby-node.js index 6a2e1d8..c54217d 100644 --- a/gatsby-node.js +++ b/gatsby-node.js @@ -17,6 +17,7 @@ const { getFilePath, parseLanguages, loadConfig, + replaceMultipleKeysInObject, } = require("./src/common") const context = require("./src/context") const queries = require("./src/queries") @@ -145,6 +146,8 @@ exports.onPreBootstrap = async ({ createContentDigest, actions, getNode }) => { hasTopConcept, member, deprecated, + "dc:title": dc_title, + "dc:description": dc_description, ...properties } = graph const type = Array.isArray(properties.type) @@ -204,6 +207,8 @@ exports.onPreBootstrap = async ({ createContentDigest, actions, getNode }) => { type, }, member___NODE: (member || []).map((member) => member.id), + dc_title, + dc_description, } if (type === "Concept") { Object.assign(node, {}) @@ -382,17 +387,29 @@ exports.createPages = async ({ graphql, actions: { createPage } }) => { customDomain: config.customDomain, }, }) + const jsonldConceptScheme = replaceMultipleKeysInObject(conceptScheme, [ + ["dc_title", "dc:title"], + ["dc_description", "dc:description"], + ]) createData({ path: getFilePath(conceptScheme.id, "json", config.customDomain), data: JSON.stringify( - omitEmpty(Object.assign({}, conceptScheme, context.jsonld), null, 2) + omitEmpty( + Object.assign({}, jsonldConceptScheme, context.jsonld), + null, + 2 + ) ), }) createData({ path: getFilePath(conceptScheme.id, "jsonld", config.customDomain), data: JSON.stringify( - omitEmpty(Object.assign({}, conceptScheme, context.jsonld), null, 2) + omitEmpty( + Object.assign({}, jsonldConceptScheme, context.jsonld), + null, + 2 + ) ), }) // create index files @@ -406,9 +423,10 @@ exports.createPages = async ({ graphql, actions: { createPage } }) => { conceptSchemes.data.allConceptScheme.edges.map(({ node: cs }) => ({ id: cs.id, title: cs.title, - dctitle: cs.dctitle, + dc_title: cs.dc_title, prefLabel: cs.prefLabel, description: cs.description, + dc_description: cs.dc_description, languages: Array.from(languagesByCS[cs.id]), })) ) diff --git a/src/common.js b/src/common.js index 9064e1a..d86efc1 100644 --- a/src/common.js +++ b/src/common.js @@ -215,6 +215,32 @@ const getLanguageFromUrl = (location) => { return language } +/** + * Replaces an oldKey against a new key + * @param {Object} obj + * @param {string} oldKey + * @param {string} newKey + * @returns {Object} + */ +const replaceKeyInObject = (obj, oldKey, newKey) => { + if (!(oldKey in obj)) return obj + const newObject = {} + delete Object.assign(newObject, obj, { [newKey]: obj[oldKey] })[oldKey] + return newObject +} + +/** + * Replaces multiple keys of an object. + * Expects an array of arrays in the form [oldKey, newKey] + */ +const replaceMultipleKeysInObject = (obj, keys) => { + const replaced = keys.reduce( + (acc, val) => replaceKeyInObject(acc, val[0], val[1]), + obj + ) + return replaced +} + module.exports = { i18n, getFilePath, @@ -225,4 +251,6 @@ module.exports = { parseLanguages, loadConfig, getLanguageFromUrl, + replaceKeyInObject, + replaceMultipleKeysInObject, } diff --git a/src/components/ConceptScheme.jsx b/src/components/ConceptScheme.jsx index 76ebf33..97b1c54 100644 --- a/src/components/ConceptScheme.jsx +++ b/src/components/ConceptScheme.jsx @@ -17,9 +17,10 @@ const ConceptScheme = ({ }, [data?.selectedLanguage]) const pathname = useLocation() - const description = conceptScheme?.description || conceptScheme?.dcdescription + const description = + conceptScheme?.description || conceptScheme?.dc_description const title = - conceptScheme?.title || conceptScheme?.dctitle || conceptScheme?.prefLabel + conceptScheme?.title || conceptScheme?.dc_title || conceptScheme?.prefLabel // got some hash uri to show if (pathname.hash) { const filtered = embed.find((c) => c.json.id.endsWith(pathname.hash)) diff --git a/src/components/header.jsx b/src/components/header.jsx index b57aec2..526fe00 100644 --- a/src/components/header.jsx +++ b/src/components/header.jsx @@ -100,6 +100,7 @@ const Header = ({ siteTitle }) => { ` const [languages, setLanguages] = useState([]) const [language, setLanguage] = useState("") + const [title, setTitle] = useState("") // set page language useEffect(() => { @@ -125,6 +126,16 @@ const Header = ({ siteTitle }) => { } }, [data]) + // set title + useEffect(() => { + const title = + data.currentScheme?.title?.[data.selectedLanguage] || + data.currentScheme?.prefLabel?.[data.selectedLanguage] || + data.currentScheme?.dc_title?.[data.selectedLanguage] || + data.currentScheme?.id + setTitle(title) + }, [data]) + return (
@@ -158,10 +169,7 @@ const Header = ({ siteTitle }) => { config.customDomain )} > - {data.currentScheme?.title?.[data.selectedLanguage] || - data.currentScheme?.prefLabel?.[data.selectedLanguage] || - data.currentScheme?.dctitle?.[data.selectedLanguage] || - data.currentScheme.id} + {title}
diff --git a/src/context.js b/src/context.js index ad93f9b..843a3cd 100644 --- a/src/context.js +++ b/src/context.js @@ -6,6 +6,7 @@ const jsonld = { "@vocab": "http://www.w3.org/2004/02/skos/core#", xsd: "http://www.w3.org/2001/XMLSchema#", dct: "http://purl.org/dc/terms/", + dc: "http://purl.org/dc/elements/1.1/", schema: "https://schema.org/", vann: "http://purl.org/vocab/vann/", ldp: "http://www.w3.org/ns/ldp#", @@ -14,12 +15,12 @@ const jsonld = { "@id": "dct:title", "@container": "@language", }, - dctitle: { - "@id": "http://purl.org/dc/elements/1.1/title", + "dc:title": { + "@id": "dc:title", "@container": "@language", }, - dcdescription: { - "@id": "http://purl.org/dc/elements/1.1/description", + "dc:description": { + "@id": "dc:description", "@container": "@language", }, description: { diff --git a/src/pages/index.js b/src/pages/index.js index 14f5ebd..bcec5c3 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -58,6 +58,20 @@ const IndexPage = ({ location }) => { }) } }, [data?.languages, data?.selectedLanguage]) + + const getTitle = (conceptScheme) => { + const title = + i18n(language)( + conceptScheme?.title || + conceptScheme?.prefLabel || + conceptScheme?.dc_title + ) || conceptScheme.id + if (title) { + return title + } + return conceptScheme.id + } + return ( @@ -78,13 +92,7 @@ const IndexPage = ({ location }) => { } to={getFilePath(conceptScheme.id, `html`, customDomain)} > - {(conceptScheme?.title && - i18n(language)(conceptScheme.title)) || - (conceptScheme?.prefLabel && - i18n(language)(conceptScheme.prefLabel)) || - (conceptScheme?.dctitle && - i18n(language)(conceptScheme.dctitle)) || - conceptScheme.id} + {getTitle(conceptScheme)} ))} diff --git a/src/queries.js b/src/queries.js index 01fc881..9270664 100644 --- a/src/queries.js +++ b/src/queries.js @@ -151,13 +151,13 @@ module.exports.allConceptScheme = (languages) => ` prefLabel { ${[...languages].join(" ")} } - dctitle { + dc_title { ${[...languages].join(" ")} } description { ${[...languages].join(" ")} } - dcdescription { + dc_description { ${[...languages].join(" ")} } hasTopConcept { diff --git a/src/templates/App.jsx b/src/templates/App.jsx index f30936d..3923cf5 100644 --- a/src/templates/App.jsx +++ b/src/templates/App.jsx @@ -158,12 +158,11 @@ const App = ({ pageContext, children, location }) => { inline: "nearest", }) }) - const toggleClick = (e) => setLabels({ ...labels, [e]: !labels[e] }) const title = pageContext.node?.prefLabel || pageContext.node?.title || - pageContext.node?.dctitle + pageContext.node?.dc_title return ( diff --git a/src/types.js b/src/types.js index b90001d..48bcd1e 100644 --- a/src/types.js +++ b/src/types.js @@ -9,10 +9,10 @@ module.exports = (languages) => ` type ConceptScheme implements Node { type: String, title: LanguageMap, - dctitle: LanguageMap, + dc_title: LanguageMap, prefLabel: LanguageMap, description: LanguageMap, - dcdescription: LanguageMap, + dc_description: LanguageMap, hasTopConcept: [Concept] @link(from: "hasTopConcept___NODE"), languages: [String] } diff --git a/test/common.test.js b/test/common.test.js index 01ec492..b637ef2 100644 --- a/test/common.test.js +++ b/test/common.test.js @@ -6,6 +6,8 @@ const { replaceFilePathInUrl, getLinkPath, getLanguageFromUrl, + replaceKeyInObject, + replaceMultipleKeysInObject, } = require("../src/common") describe("Translate", () => { @@ -91,3 +93,28 @@ describe("getLanguageFromUrl", () => { expect(getLanguageFromUrl(location)).toBeNull() }) }) + +describe("replaceKeysInObject", () => { + it("replaces key in an object", () => { + const obj = { a: 1, b: 2 } + const newObj = replaceKeyInObject(obj, "a", "c") + expect(newObj).toStrictEqual({ c: 1, b: 2 }) + }) + + it("also works if the key is not present", () => { + const obj = { a: 1, b: 2 } + const newObj = replaceKeyInObject(obj, "x", "c") + expect(newObj).toStrictEqual({ a: 1, b: 2 }) + }) +}) + +describe("replaceMultipleKeysInObject", () => { + it("replaces multiple keys in an object", () => { + const obj = { a: 1, b: 2 } + const newObj = replaceMultipleKeysInObject(obj, [ + ["a", "c"], + ["b", "d"], + ]) + expect(newObj).toStrictEqual({ c: 1, d: 2 }) + }) +}) diff --git a/test/conceptScheme.test.jsx b/test/conceptScheme.test.jsx index 30a5be4..6514dab 100644 --- a/test/conceptScheme.test.jsx +++ b/test/conceptScheme.test.jsx @@ -100,7 +100,7 @@ describe.concurrent("Concept", () => { ...ConceptSchemePC, node: { ...ConceptSchemePC.node, - dctitle: { + dc_title: { de: "dctitle DE", }, }, diff --git a/test/data/ttl/slashURIConceptSchemeDCproperties.ttl b/test/data/ttl/slashURIConceptSchemeDCproperties.ttl new file mode 100644 index 0000000..b6fdbaa --- /dev/null +++ b/test/data/ttl/slashURIConceptSchemeDCproperties.ttl @@ -0,0 +1,46 @@ +@base . +@prefix dct: . +@prefix dc: . +@prefix skos: . +@prefix schema: . +@prefix vann: . + +<> a skos:ConceptScheme ; + dc:title "Test Vokabular DC"@de, "Test Vocabulary DC"@en ; + dc:description "Test Beschreibung DC"@de, "Test Description DC"@en ; + dct:issued "2019-12-11" ; + dct:publisher ; + skos:hasTopConcept . + + a skos:Concept ; + skos:prefLabel "Konzept 1"@de, "Concept 1"@en ; + skos:altLabel "Alternativbezeichnung 1"@de, "Alternativbezeichnung 2"@de, "Alt Label 1"@en ; + skos:hiddenLabel "Verstecktes Label 1"@de, "Verstecktes Label 2"@de ; + skos:definition "Meine Definition"@de ; + skos:example "Ein Beispiel"@de ; + skos:scopeNote "Meine Scope Note"@de ; + skos:note "Meine Anmerkung"@de ; + skos:relatedMatch ; + skos:narrower ; + skos:notation "1", "notation" ; + skos:topConceptOf <> . + + a skos:Concept ; + skos:prefLabel "Konzept 2"@de, "Concept 2"@en ; + skos:narrower ; + skos:broader ; + skos:inScheme <> . + + a skos:Concept ; + skos:prefLabel "Konzept 3"@de ; + skos:broader ; + skos:inScheme <> . + + a skos:Concept ; + skos:prefLabel "Konzept 4"@de ; + skos:broader ; + skos:inScheme <> . + + a skos:Collection ; + skos:prefLabel "Meine Collection"@de, "My Collection"@en ; + skos:member , .