Skip to content
Snippets Groups Projects
Commit 433d5c85 authored by Reiter, Christoph's avatar Reiter, Christoph :snake:
Browse files

Add a new resource-select package/web-component

The goal of this new component is to be a generic selector for API resources,
unlike the organization/person select, which hardcode various things for their
used resource.

Instead this provides some events which can be used to customize the creation of the
URL for fetching as well as for formatting the list of results.

Atm this only handles the organization and no search-as-you-type like the person select,
but maybe this can be extended in the future.
parent c595eccf
Branches
No related tags found
1 merge request!161Add a new resource-select package/web-component
Pipeline #96379 passed
Showing
with 951 additions and 0 deletions
/vendor/**
/dist/**
/*.js
\ No newline at end of file
{
"root": true,
"extends": "./../../eslint.common.json"
}
dist
node_modules
.idea
npm-debug.log
package-lock.json
node_modules
.idea
npm-debug.log
package-lock.json
index.html
node_modules/
/dist
/vendor
{
"bracketSpacing": false,
"singleQuote": true,
"tabWidth": 4,
"printWidth": 100,
"bracketSameLine": true,
"htmlWhitespaceSensitivity": "ignore",
"overrides": [
{
"files": "*.js",
"options": {
"semi": true
}
}
]
}
This diff is collapsed.
# Resource Select Web Component
You can install this component via npm:
```bash
npm i @dbp-toolkit/resource-select
```
## Usage
```html
<dbp-resource-select></dbp-resource-select>
<script type="module" src="node_modules/@dbp-toolkit/resource-select/dist/dbp-resource-select.js"></script>
```
Or directly via CDN:
```html
<dbp-resource-select></dbp-resource-select>
<script type="module" src="https://unpkg.com/@dbp-toolkit/resource-select@latest/dist/dbp-resource-select.js"></script>
```
## Attributes
- `lang` (optional, default: `de`): set to `de` or `en` for German or English
- example `<dbp-resource-select lang="de"></dbp-resource-select>`
- `entry-point-url`: entry point url to access the api
- example `<dbp-resource-select entry-point-url="http://127.0.0.1:8000"></dbp-resource-select>`
- `resource-path` (optional): path to the resource
- example `<dbp-resource-select resource-path="base/people"></dbp-resource-select>`
- `value` (optional): api path of recource to preload the selector with
- example `<dbp-resource-select value="/base/people/testuser"></dbp-resource-select>`
- the `value` will also be set automatically when an organization is chosen in the selector
## Properties
- `valueObject` (optional): The resource object corresponding to `value`
- `auth` {object}: you need to set that object property for the auth token
- example auth property: `{token: "THE_BEARER_TOKEN"}`
- note: most often this should be a property that is not set directly, but subscribed at a provider
## Events
- `change` - Gets dispatched when either `value` or `valueObject` change.
- `event.detail.value` - Same as the `value` property
- `event.detail.object` - Same as the `valueObject` property
- `build-url` - Gets dispatched when during URL building.
- `event.detail.url` - Set this to the URL representing the collection endpoint
- `format-resource` - Gets dispatched when creating the display test for earch resource.
- `event.detail.object` - The resource that which should be represented by the text
- `event.detail.text` - Set this to the text that should be displayed
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<script type="module" src="dbp-resource-select-demo.js"></script>
</head>
<body>
<dbp-resource-select-demo auth requested-login-status lang="de" entry-point-url="http://127.0.0.1:8000"></dbp-resource-select-demo>
</body>
</html>
<html>
<body>
<script>
parent.postMessage(location.href, location.origin)
</script>
</body>
</html>
\ No newline at end of file
module.exports = {
input: ['src/*.js'],
output: './',
options: {
debug: false,
removeUnusedKeys: true,
func: {list: ['i18n.t', '_i18n.t']},
lngs: ['en', 'de'],
resource: {
loadPath: 'src/i18n/{{lng}}/{{ns}}.json',
savePath: 'src/i18n/{{lng}}/{{ns}}.json',
},
},
};
module.exports = require('../../karma.common.conf.js');
{
"name": "@dbp-toolkit/resource-select",
"homepage": "https://gitlab.tugraz.at/dbp/web-components/toolkit/-/tree/master/packages/resource-select",
"version": "0.1.0",
"main": "src/index.js",
"license": "LGPL-2.1-or-later",
"repository": {
"type": "git",
"url": "https://gitlab.tugraz.at/dbp/web-components/toolkit.git",
"directory": "packages/resource-select"
},
"publishConfig": {
"registry": "https://registry.npmjs.org",
"access": "public"
},
"devDependencies": {
"@esm-bundle/chai": "^4.2.0",
"@rollup/plugin-commonjs": "^21.0.0",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.0.0",
"@rollup/plugin-replace": "^4.0.0",
"@rollup/plugin-url": "^6.0.0",
"eslint": "^8.0.0",
"eslint-plugin-jsdoc": "^38.0.0",
"i18next-scanner": "^3.0.0",
"karma": "^6.0.0",
"karma-chrome-launcher": "^3.0.0",
"karma-firefox-launcher": "^2.1.0",
"karma-mocha": "^2.0.1",
"mocha": "^9.0.0",
"prettier": "^2.5.1",
"rollup": "^2.33.3",
"rollup-plugin-copy": "^3.1.0",
"rollup-plugin-delete": "^2.0.0",
"rollup-plugin-serve": "^1.0.1",
"rollup-plugin-terser": "^7.0.2"
},
"dependencies": {
"@dbp-toolkit/auth": "^0.3.0",
"@dbp-toolkit/common": "^0.3.0",
"@open-wc/scoped-elements": "^2.0.0",
"jquery": "^3.4.1",
"lit": "^2.0.0",
"select2": "^4.0.10"
},
"scripts": {
"clean": "rm dist/*",
"format": "yarn run format:eslint && yarn run format:prettier",
"format:eslint": "eslint \"**/*.{js,ts}\" --fix",
"format:prettier": "prettier \"**/*.{js,json,ts}\" --write",
"build": "npm run build-local",
"build-local": "rollup -c",
"build-dev": "rollup -c --environment BUILD:development",
"build-prod": "rollup -c --environment BUILD:production",
"build-demo": "rollup -c --environment BUILD:demo",
"build-test": "rollup -c --environment BUILD:test",
"i18next": "i18next-scanner",
"watch": "npm run watch-local",
"watch-local": "rollup -c --watch",
"watch-dev": "rollup -c --watch --environment BUILD:development",
"test": "npm run build-test && karma start --singleRun",
"lint": "eslint ."
}
}
import glob from 'glob';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import copy from 'rollup-plugin-copy';
import {terser} from 'rollup-plugin-terser';
import json from '@rollup/plugin-json';
import serve from 'rollup-plugin-serve';
import url from '@rollup/plugin-url';
import del from 'rollup-plugin-delete';
import {getPackagePath, getDistPath} from '../../rollup.utils.js';
const build = typeof process.env.BUILD !== 'undefined' ? process.env.BUILD : 'local';
console.log('build: ' + build);
export default (async () => {
return {
input:
build != 'test'
? ['src/dbp-resource-select.js', 'src/dbp-resource-select-demo.js']
: glob.sync('test/**/*.js'),
output: {
dir: 'dist',
entryFileNames: '[name].js',
chunkFileNames: 'shared/[name].[hash].[format].js',
format: 'esm',
sourcemap: true,
},
plugins: [
del({
targets: 'dist/*',
}),
resolve(),
commonjs(),
url({
limit: 0,
include: [await getPackagePath('select2', '**/*.css')],
emitFiles: true,
fileName: 'shared/[name].[hash][extname]',
}),
json(),
build !== 'local' && build !== 'test' ? terser() : false,
copy({
targets: [
{src: 'assets/index.html', dest: 'dist'},
{src: 'assets/silent-check-sso.html', dest: 'dist'},
{
src: await getPackagePath('@dbp-toolkit/common', 'assets/icons/*.svg'),
dest: 'dist/' + (await getDistPath('@dbp-toolkit/common', 'icons')),
},
],
}),
process.env.ROLLUP_WATCH === 'true'
? serve({contentBase: 'dist', host: '127.0.0.1', port: 8002})
: false,
],
};
})();
import {createInstance} from './i18n.js';
import {css, html} from 'lit';
import {ScopedElementsMixin} from '@open-wc/scoped-elements';
import {ResourceSelect} from './resource-select.js';
import {AuthKeycloak, LoginButton} from '@dbp-toolkit/auth';
import * as commonUtils from '@dbp-toolkit/common/utils';
import * as commonStyles from '@dbp-toolkit/common/styles';
import DBPLitElement from '@dbp-toolkit/common/dbp-lit-element';
export class ResourceSelectDemo extends ScopedElementsMixin(DBPLitElement) {
constructor() {
super();
this._i18n = createInstance();
this.lang = this._i18n.language;
this.entryPointUrl = '';
this.noAuth = false;
}
static get scopedElements() {
return {
'dbp-auth-keycloak': AuthKeycloak,
'dbp-login-button': LoginButton,
'dbp-resource-select': ResourceSelect,
};
}
static get properties() {
return {
...super.properties,
lang: {type: String},
entryPointUrl: {type: String, attribute: 'entry-point-url'},
noAuth: {type: Boolean, attribute: 'no-auth'},
};
}
update(changedProperties) {
if (changedProperties.has('lang')) {
this._i18n.changeLanguage(this.lang);
}
super.update(changedProperties);
}
static get styles() {
// language=css
return [
commonStyles.getThemeCSS(),
commonStyles.getGeneralCSS(),
css`
h1.title {
margin-bottom: 1em;
}
div.container {
margin-bottom: 1.5em;
}
`,
];
}
getAuthComponentHtml() {
return this.noAuth
? html`
<dbp-login-button subscribe="auth" lang="${this.lang}"></dbp-login-button>
`
: html`
<div class="container">
<dbp-auth-keycloak
subscribe="requested-login-status"
lang="${this.lang}"
entry-point-url="${this.entryPointUrl}"
silent-check-sso-redirect-uri="/silent-check-sso.html"
url="https://auth-dev.tugraz.at/auth"
realm="tugraz-vpu"
client-id="auth-dev-mw-frontend-local"
try-login></dbp-auth-keycloak>
<dbp-login-button subscribe="auth" lang="${this.lang}"></dbp-login-button>
</div>
`;
}
render() {
let buildUrl = (event) => {
let select = event.target;
let url = new URL(select.resourcePath, select.entryPointUrl).href;
url += '/' + encodeURIComponent(select.auth['person-id']);
url += '/organizations';
url += '?' + new URLSearchParams({lang: select.lang}).toString();
event.detail.url = url;
};
let formatResource = (event) => {
event.detail.text = event.detail.object.name;
};
let change = (event) => {
console.log('change:', event.detail.value, event.detail.object);
};
return html`
<section class="section">
<div class="container">
<h1 class="title">resource-select-Demo</h1>
</div>
${this.getAuthComponentHtml()}
<div class="container">
<form>
<div class="field">
<label class="label">Organization of the current user</label>
<div class="control">
<dbp-resource-select
id="resource-select-library-manager"
subscribe="auth"
lang="${this.lang}"
entry-point-url="${this.entryPointUrl}"
resource-path="base/people"
@change="${change}"
@build-url="${buildUrl}"
@format-resource="${formatResource}"></dbp-resource-select>
</div>
</div>
</form>
</div>
</section>
`;
}
}
commonUtils.defineCustomElement('dbp-resource-select-demo', ResourceSelectDemo);
import * as commonUtils from '@dbp-toolkit/common/utils';
import {ResourceSelect} from './resource-select.js';
commonUtils.defineCustomElement('dbp-resource-select', ResourceSelect);
import './dbp-resource-select-demo.js';
/**
* @param {string} url
* @param {string} token
* @returns {Array}
*/
export async function getCollection(url, token) {
let response = await fetch(url, {
headers: {
'Content-Type': 'application/ld+json',
Authorization: 'Bearer ' + token,
},
});
if (!response.ok) {
throw new Error(response.statusText);
}
let data = await response.json();
return data['hydra:member'];
}
import {createInstance as _createInstance} from '@dbp-toolkit/common/i18next.js';
import de from './i18n/de/translation.json';
import en from './i18n/en/translation.json';
export function createInstance() {
return _createInstance({en: en, de: de}, 'de', 'en');
}
/**
* Content from https://github.com/select2/select2/blob/master/src/js/select2/i18n/de.js
*/
export default function () {
// German
return {
errorLoading: function () {
return 'Die Ergebnisse konnten nicht geladen werden.';
},
inputTooLong: function (args) {
var overChars = args.input.length - args.maximum;
return 'Bitte ' + overChars + ' Zeichen weniger eingeben';
},
inputTooShort: function (args) {
var remainingChars = args.minimum - args.input.length;
return 'Bitte ' + remainingChars + ' Zeichen mehr eingeben';
},
loadingMore: function () {
return 'Lade mehr Ergebnisse…';
},
maximumSelected: function (args) {
var message = 'Sie können nur ' + args.maximum + ' Eintr';
if (args.maximum === 1) {
message += 'ag';
} else {
message += 'äge';
}
message += ' auswählen';
return message;
},
noResults: function () {
return 'Keine Übereinstimmungen gefunden';
},
searching: function () {
return 'Suche…';
},
removeAllItems: function () {
return 'Auswahl aufheben';
},
};
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment