Skip to content

Commit

Permalink
feat: add base iri attribution to skosConceptScheme
Browse files Browse the repository at this point in the history
  • Loading branch information
andybywire committed May 9, 2023
2 parents 622a1a3 + 25e500c commit 2af233e
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 72 deletions.
11 changes: 9 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- People can see what changes they might expect in upcoming releases
- At release time, you can move the Unreleased section changes into a new release version section.
## Commit Message Prefixes
### feat: a new feature for the user
### fix: a bug fix for the user
### chore: changes that aren't a fix or feature and don't modify src or test files
### docs: changes to the documentation
### style: formatting changes that do not affect the meaning of the code
### test: adding or refactoring tests
### Release Process
1. Safety Checks:
- git pull
Expand All @@ -175,7 +183,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- git push
- git push --tags
7. Create a GitHub Release (optional)
8. Update plugin in staging studio, run `sanity deploy`, and validate published package.
8. Update (reinstall) plugin in staging studio, run `sanity deploy`, and validate published package.
source: https://cloudfour.com/thinks/how-to-publish-an-updated-version-of-an-npm-package/
--->
3 changes: 2 additions & 1 deletion src/components/PrefLabel.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/**
* SKOS Concept Preferred Label input component
* Used for Concept and Concept Scheme URI preview
*/

import {Stack, Text} from '@sanity/ui'
Expand All @@ -13,7 +14,7 @@ export function PrefLabel(props: PrefLabelValue) {
{props.renderDefault(props)}
<Text muted size={1} onResize={undefined} onResizeCapture={undefined}>
<>
<strong>Concept IRI: </strong>
<strong>URI: </strong>
{baseIri ? baseIri : '[base URI not defined] '}
{props.value?.replaceAll(' ', '')}
</>
Expand Down
35 changes: 35 additions & 0 deletions src/modules/baseIriField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {defineField} from 'sanity'
import {DescriptionDetail} from '../styles'

export default [
defineField({
name: 'baseIri',
title: 'Base URI',
type: 'url',
validation: (Rule) =>
Rule.required().error('Please supply a base URI in the format http://example.com/'),
description: (
<details>
<summary>
The root URI (Uniform Resource Identifier) used to create unique concept identifiers.
</summary>
<DescriptionDetail>
Unique identifiers allow for the clear an unambiguous identification of concepts across
namespaces, for example between https://shipparts.com/vocab#Bow and
https://wrappingsupplies.com/vocab#Bow.
</DescriptionDetail>
<DescriptionDetail>
In most cases, it makes sense for your base URI to be the root or a subdirectory of your
website. In all cases, the URI you choose should be in a domain that you control.
</DescriptionDetail>
<DescriptionDetail>
For new Concepts and Concept Schemes, the Base URI field is pre-populated based on the
most recently used Base URI value.
</DescriptionDetail>
</details>
),
options: {
collapsible: true,
},
}),
]
99 changes: 31 additions & 68 deletions src/skosConcept.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
// Sanity document scheme for SKOS Taxonomy Concepts
// @todo Improve typings
// @todo Hierarchy, Broader, & Associated: enforce disjointedness between Associated and BroaderTransitive (integrity constraint); prohibit cycles in hierarchical relations (best practice).
// 2022-03-31: Filtering added to Related to five levels of hierarchy, document filtering present for Broader. Consider more robust filtering and validation for future releases.
// @todo Document level validation for the disjunction between Preferred, Alternate, and Hidden Labels
// @todo Lexical labels: add child level validation so that offending labels are shown directly when a duplicate is entered. Then consider removing document level validation. cf. https://www.sanity.io/docs/validation#9e69d5db6f72
// @todo Scheme initial value: Configure "default" option in Concept Scheme, for cases when there are multiple schemes; configure initialValue to default to that selection (It's currently configure to take the scheme ordered first. This isn't transparent.)
// @todo Abstract broader and related concept filter into reusable function, and/or add in validation to cover wider scenarios.
//
/**
* Sanity document scheme for SKOS Taxonomy Concepts
* @todo Improve typings
* @todo Hierarchy, Broader, & Associated: enforce disjointedness between Associated and BroaderTransitive (integrity constraint); prohibit cycles in hierarchical relations (best practice).
* 2022-03-31: Filtering added to Related to five levels of hierarchy, document filtering present for Broader. Consider more robust filtering and validation for future releases.
* @todo Document level validation for the disjunction between Preferred, Alternate, and Hidden Labels
* @todo Lexical labels: add child level validation so that offending labels are shown directly when a duplicate is entered. Then consider removing document level validation. cf. https://www.sanity.io/docs/validation#9e69d5db6f72
* @todo Scheme initial value: Configure "default" option in Concept Scheme, for cases when there are multiple schemes; configure initialValue to default to that selection (It's currently configure to take the scheme ordered first. This isn't transparent.)
* @todo Abstract broader and related concept filter into reusable function, and/or add in validation to cover wider scenarios.
*/

// import config from 'config:taxonomy-manager'
import {AiFillTag, AiFillTags} from 'react-icons/ai'
import {defineType, defineField} from 'sanity'
import {PrefLabel} from './components/PrefLabel'
import baseIriField from './modules/baseIriField'

export default defineType({
name: 'skosConcept',
Expand All @@ -31,26 +33,10 @@ export default defineType({
related: [], // an empty array is needed here in order to return concepts with no "broader" for "related"
}
},
groups: [
{
name: 'label',
title: 'Labels',
default: true,
},
{
name: 'relationship',
title: 'Relationships',
},
{
name: 'note',
title: 'Documentation',
},
],
fields: [
defineField({
name: 'prefLabel',
title: 'Preferred Label',
group: 'label',
type: 'string',
description:
'The preferred lexical label for this concept. This label is also used to unambiguously represent this concept via the concept IRI.',
Expand All @@ -74,53 +60,18 @@ export default defineType({
})
}),
}),
defineField({
name: 'baseIri',
title: 'Base IRI',
type: 'url',
group: 'label',
validation: (Rule) =>
Rule.required().error('Please supply a base IRI in the format http://example.com/'),
description:
'Base IRI is the root IRI (Internationalized Resource Identifier) used to create unique concept identifiers. Unique identifiers allow for the clear an unambiguous identification of concepts across namespaces, for example between https://shipparts.com/vocab#Bow and https://wrappingsupplies.com/vocab#Bow. ',
// 'The W3C encourages the use of HTTP URIs when minting concept URIs since they are resolvable to representations that can be accessed using standard Web technologies.',
options: {
collapsible: true,
},
}),
...baseIriField,
defineField({
name: 'conceptIriBase',
title: 'Edit the base IRI',
type: 'baseIri',
group: 'label',
// type: 'string'
}),
defineField({
name: 'altLabel',
title: 'Alternate Label(s)',
group: 'label',
type: 'array',
description:
'Alternative labels can be used to assign synonyms, near-synonyms, abbreviations, and acronyms to a concept. Preferred, alternative, and hidden label sets must not overlap.',
of: [{type: 'string'}],
validation: (Rule) => Rule.unique(),
}),
defineField({
name: 'hiddenLabel',
title: 'Hidden Label(s)',
group: 'label',
type: 'array',
description:
'Hidden labels are for character strings that need to be accessible to applications performing text-based indexing and search operations, but not visible otherwise. Hidden labels may for instance be used to include misspelled variants of other lexical labels. Preferred, alternative, and hidden label sets must not overlap.',
of: [{type: 'string'}],
validation: (Rule) => Rule.unique(),
// this field is visible only if a conceptIriBase using the old scheme is present
}),
defineField({
name: 'broader',
title: 'Broader Concept(s)',
description:
'Broader relationships create a hierarchy between concepts, for example to create category/subcategory, part/whole, or class/instance relationships.',
group: 'relationship',
type: 'array',
of: [
{
Expand Down Expand Up @@ -149,7 +100,6 @@ export default defineType({
title: 'Related Concept(s)',
description:
'Associative links between concepts indicate that the two are inherently "related", but that one is not in any way more general than the other. Broader and Associated relationships are mutually exclusive.',
group: 'relationship',
type: 'array',
of: [
{
Expand All @@ -158,35 +108,49 @@ export default defineType({
},
],
}),
defineField({
name: 'altLabel',
title: 'Alternate Label(s)',
type: 'array',
description:
'Alternative labels can be used to assign synonyms, near-synonyms, abbreviations, and acronyms to a concept. Preferred, alternative, and hidden label sets must not overlap.',
of: [{type: 'string'}],
validation: (Rule) => Rule.unique(),
}),
defineField({
name: 'hiddenLabel',
title: 'Hidden Label(s)',
type: 'array',
description:
'Hidden labels are for character strings that need to be accessible to applications performing text-based indexing and search operations, but not visible otherwise. Hidden labels may for instance be used to include misspelled variants of other lexical labels. Preferred, alternative, and hidden label sets must not overlap.',
of: [{type: 'string'}],
validation: (Rule) => Rule.unique(),
}),
defineField({
name: 'scopeNote',
title: 'Scope Note',
type: 'text',
description:
'A brief statement on the intended meaning of this concept, especially as an indication of how the use of the concept is limited in indexing practice',
rows: 3,
group: 'note',
}),
defineField({
name: 'definition',
title: 'Definition',
type: 'text',
description: 'A complete explanation of the intended meaning of the concept',
rows: 3,
group: 'note',
}),
defineField({
name: 'example',
title: 'Examples',
type: 'text',
description: 'An example of the use of the concept.',
rows: 3,
group: 'note',
}),
defineField({
name: 'topConcept',
title: 'Top Concept',
group: 'relationship',
type: 'boolean',
description: (
<>
Expand All @@ -205,7 +169,6 @@ export default defineType({
defineField({
name: 'scheme',
title: 'Concept Scheme(s)',
group: 'relationship',
type: 'reference',
hidden: ({document}) => !document?.scheme,
description: (
Expand Down
21 changes: 20 additions & 1 deletion src/skosConceptScheme.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,46 @@
/**
* Sanity document scheme for SKOS Concept Schemes
* @todo Add BaseIri field
* @todo Add BaseIri field ✔︎
* @todo Add administrative metadata: dc:title, dc:author ... date, last revised, etc.
* @todo Add support for sorting array lists alphabetically (custom component?)
* @todo Consider adding informational lists to this view (via custom input component): number of terms, list of terms, links.
*/

import {RiNodeTree} from 'react-icons/ri'
import {defineArrayMember, defineField, defineType} from 'sanity'
import {PrefLabel} from './components/PrefLabel'
import baseIriField from './modules/baseIriField'

export default defineType({
name: 'skosConceptScheme',
title: 'Concept Scheme',
type: 'document',
icon: RiNodeTree,
initialValue: async (props, context) => {
const {getClient} = context
const client = getClient({apiVersion: '2021-03-25'})
const baseIri =
(await client.fetch(`
*[_type == 'skosConceptScheme' && defined(baseIri)]| order(_createdAt desc)[0].baseIri
`)) ?? undefined
return {
baseIri: baseIri,
broader: [], // an empty array is needed here in order to return concepts with no "broader" for "related"
related: [], // an empty array is needed here in order to return concepts with no "broader" for "related"
}
},
fields: [
defineField({
name: 'title',
title: 'Title',
type: 'string',
description:
'Taxonomy schemes group concepts into defined sets, such as thesauri, classification schemes, or facets. Concepts may belong on many (or no) concept schemes, and you may create as many (or few) concept schemes as you like',
components: {
input: PrefLabel as any,
},
}),
...baseIriField,
defineField({
name: 'description',
title: 'Description',
Expand Down
6 changes: 6 additions & 0 deletions src/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import styled from 'styled-components'

export const DescriptionDetail = styled.p`
margin-top: 0.5rem;
margin-left: 0.75rem;
`

0 comments on commit 2af233e

Please sign in to comment.