Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions __mocks__/contacts-rdflib.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
type AddressBookContact = {
uri: string
name: string
}

type AddressBook = {
contacts: AddressBookContact[]
}

type AddressBookList = {
publicUris: string[]
privateUris: string[]
}

export default class ContactsModuleRdfLib {
constructor (_options: unknown) {}

async listAddressBooks (_webId: string): Promise<AddressBookList> {
return {
publicUris: [],
privateUris: []
}
}

async readAddressBook (_addressBookUri: string): Promise<AddressBook> {
return {
contacts: []
}
}
}

export type { AddressBook }
5 changes: 4 additions & 1 deletion jest.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ export default {
'^.+\\.(mjs|[tj]sx?)$': ['babel-jest', { configFile: './babel.config.mjs' }],
},
transformIgnorePatterns: [
'<rootDir>/node_modules/(?!(lit-html|@noble/curves|@noble/hashes|@exodus/bytes|uuid|jsdom|parse5|@asamuzakjp/css-color|@csstools)/)',
'<rootDir>/node_modules/(?!(lit-html|@noble/curves|@noble/hashes|@exodus/bytes|uuid|jsdom|parse5|@asamuzakjp/css-color|@csstools|@solid-data-modules/contacts-rdflib)/)',
],
moduleNameMapper: {
'^@solid-data-modules/contacts-rdflib$': '<rootDir>/__mocks__/contacts-rdflib.ts',
},
setupFilesAfterEnv: ['./test/helpers/setup.ts'],
testMatch: ['**/?(*.)+(spec|test).[tj]s?(x)'],
roots: ['<rootDir>/src', '<rootDir>/test', '<rootDir>/__mocks__'],
Expand Down
35 changes: 35 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"dependencies": {
"@noble/curves": "^2.0.1",
"@noble/hashes": "^2.0.1",
"@solid-data-modules/contacts-rdflib": "^0.7.1",
"escape-html": "^1.0.3",
"mime-types": "^3.0.2",
"pane-registry": "^3.0.2",
Expand Down
190 changes: 190 additions & 0 deletions src/stories/PeopleSearch.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import * as UI from '../../src/index'
import ContactsModuleRdfLib from '@solid-data-modules/contacts-rdflib'

const CATALOG_URL =
'https://raw.githubusercontent.com/solid/catalog/refs/heads/main/catalog-data.ttl'

function makeMockKb () {
const meWebId = 'https://demo.example/profile/card#me'
const friendWebId = 'https://friend.example/profile/card#me'
const personFromKnowsWebId = 'https://person.example/profile/card#me'
const contactCardUri = 'https://demo.example/contacts/alice#this'

const namesByWebId = {
[friendWebId]: 'Frank Friend',
[personFromKnowsWebId]: 'Pat Person'
}

return {
fetcher: {
load: async function () {
return undefined
}
},
updater: {},
any: function (subject, predicate) {
const subjectValue = subject && subject.value
const predicateValue = predicate && predicate.value

if (!subjectValue || !predicateValue) {
return null
}

if (
predicateValue.includes('foaf/0.1/name') ||
predicateValue.endsWith('#name') ||
predicateValue.includes('/2006/vcard/ns#fn')
) {
const personName = namesByWebId[subjectValue]
return personName ? { value: personName } : null
}

if (
predicateValue.includes('/2006/vcard/ns#url') &&
subjectValue === contactCardUri
) {
return $rdf.namedNode(`${contactCardUri}-url`)
}

return null
},
anyValue: function (subject, predicate) {
const subjectValue = subject && subject.value
const predicateValue = predicate && predicate.value

if (!subjectValue || !predicateValue) {
return null
}

if (
predicateValue.includes('/2006/vcard/ns#value') &&
subjectValue === `${contactCardUri}-url`
) {
return 'https://alice.example/profile/card#me'
}

return null
},
each: function (subject, predicate) {
const subjectValue = subject && subject.value
const predicateValue = predicate && predicate.value

if (!subjectValue || !predicateValue) {
return []
}

if (!predicateValue.includes('foaf/0.1/knows')) {
return []
}

if (subjectValue === meWebId) {
return [
$rdf.namedNode(friendWebId),
$rdf.namedNode(personFromKnowsWebId)
]
}

return []
}
}
}

function installPeopleSearchMocks () {
const originalListAddressBooks =
ContactsModuleRdfLib.prototype.listAddressBooks
const originalReadAddressBook =
ContactsModuleRdfLib.prototype.readAddressBook
const originalFetch = globalThis.fetch

ContactsModuleRdfLib.prototype.listAddressBooks = async function () {
return {
publicUris: ['https://demo.example/address-book#this'],
privateUris: []
}
}

ContactsModuleRdfLib.prototype.readAddressBook = async function () {
return {
contacts: [
{
uri: 'https://demo.example/contacts/alice#this',
name: 'Alice Contact'
}
]
}
}

globalThis.fetch = async function (input, init) {
const url = typeof input === 'string' ? input : input && input.url

if (url === CATALOG_URL) {
return {
ok: true,
status: 200,
text: async function () {
return `
@prefix ex: <http://example.org#> .

<https://catalog-person.example/profile/card#me> a ex:Person ;
ex:name "Catalog Person" ;
ex:webid <https://catalog-person.example/profile/card#me> .
`
}
}
}

if (typeof originalFetch === 'function') {
return originalFetch(input, init)
}

return {
ok: false,
status: 404,
text: async function () {
return ''
}
}
}

return function restoreMocks () {
ContactsModuleRdfLib.prototype.listAddressBooks = originalListAddressBooks
ContactsModuleRdfLib.prototype.readAddressBook = originalReadAddressBook
globalThis.fetch = originalFetch
}
}

export default {
title: 'Widgets/PeopleSearch'
}

export const SignedOut = {
render: () => {
return UI.widgets.createPeopleSearch(document, makeMockKb(), null)
},
name: 'signed out'
}

export const WithMockData = {
render: () => {
const restoreMocks = installPeopleSearchMocks()

const wrapper = document.createElement('div')
const info = document.createElement('p')
info.textContent =
'Mocked sources: address book, foaf:knows, and Solid catalog. Type to filter.'
wrapper.appendChild(info)

const me = $rdf.namedNode('https://demo.example/profile/card#me')
const picker = UI.widgets.createPeopleSearch(document, makeMockKb(), me)
wrapper.appendChild(picker)

const cleanup = function () {
restoreMocks()
wrapper.removeEventListener('DOMNodeRemovedFromDocument', cleanup)
}
wrapper.addEventListener('DOMNodeRemovedFromDocument', cleanup)

return wrapper
},
name: 'with mock data'
}
1 change: 1 addition & 0 deletions src/widgets/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
// export widgets with the same name)

export * from './peoplePicker'
export * from './peopleSearch'
export * from './dragAndDrop'
export * from './buttons'
export * from './buttons/iconLinks'
Expand Down
Loading
Loading