@knowark/componarkjs 1.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Makefile +49 -0
- package/README.md +47 -0
- package/knowarkjs.code-workspace +29 -0
- package/lib/base/component/README.rst +113 -0
- package/lib/base/component/component.js +115 -0
- package/lib/base/component/component.test.js +327 -0
- package/lib/base/component/index.js +3 -0
- package/lib/base/index.js +1 -0
- package/lib/base/styles/index.js +3 -0
- package/lib/base/styles/styles.js +320 -0
- package/lib/base/utils/define.js +21 -0
- package/lib/base/utils/define.test.js +62 -0
- package/lib/base/utils/format.js +24 -0
- package/lib/base/utils/format.test.js +19 -0
- package/lib/base/utils/helpers.js +96 -0
- package/lib/base/utils/helpers.test.js +154 -0
- package/lib/base/utils/index.js +5 -0
- package/lib/base/utils/slots.js +18 -0
- package/lib/base/utils/slots.test.js +52 -0
- package/lib/base/utils/uuid.js +9 -0
- package/lib/base/utils/uuid.test.js +19 -0
- package/lib/components/audio/README.md +22 -0
- package/lib/components/audio/components/audio.js +103 -0
- package/lib/components/audio/components/audio.test.js +127 -0
- package/lib/components/audio/index.js +1 -0
- package/lib/components/audio/styles/ark.css.js +83 -0
- package/lib/components/audio/styles/index.js +2 -0
- package/lib/components/camera/README.md +64 -0
- package/lib/components/camera/components/camera.js +85 -0
- package/lib/components/camera/components/camera.test.js +104 -0
- package/lib/components/camera/index.js +1 -0
- package/lib/components/camera/styles/ark.css.js +17 -0
- package/lib/components/camera/styles/index.js +2 -0
- package/lib/components/capture/components/capture.js +54 -0
- package/lib/components/capture/components/capture.test.js +112 -0
- package/lib/components/capture/index.js +1 -0
- package/lib/components/droparea/README.md +51 -0
- package/lib/components/droparea/components/droparea-preview.js +159 -0
- package/lib/components/droparea/components/droparea-preview.test.js +105 -0
- package/lib/components/droparea/components/droparea.js +165 -0
- package/lib/components/droparea/components/droparea.test.js +320 -0
- package/lib/components/droparea/index.js +1 -0
- package/lib/components/droparea/styles/ark.css.js +235 -0
- package/lib/components/droparea/styles/index.js +3 -0
- package/lib/components/emit/components/emit.js +33 -0
- package/lib/components/emit/components/emit.test.js +138 -0
- package/lib/components/emit/index.js +1 -0
- package/lib/components/index.js +9 -0
- package/lib/components/list/README.md +103 -0
- package/lib/components/list/components/item.test.js +93 -0
- package/lib/components/list/components/list.item.js +22 -0
- package/lib/components/list/components/list.js +96 -0
- package/lib/components/list/components/list.test.js +267 -0
- package/lib/components/list/index.js +2 -0
- package/lib/components/paginator/README.md +32 -0
- package/lib/components/paginator/components/paginator.js +110 -0
- package/lib/components/paginator/components/paginator.test.js +131 -0
- package/lib/components/paginator/index.js +1 -0
- package/lib/components/paginator/styles/ark.css.js +196 -0
- package/lib/components/paginator/styles/index.js +2 -0
- package/lib/components/spinner/README.md +41 -0
- package/lib/components/spinner/components/spinner.js +105 -0
- package/lib/components/spinner/components/spinner.test.js +50 -0
- package/lib/components/spinner/index.js +1 -0
- package/lib/components/spinner/styles/ark.css.js +658 -0
- package/lib/components/spinner/styles/index.js +2 -0
- package/lib/components/splitview/README.md +63 -0
- package/lib/components/splitview/components/splitview.detail.js +46 -0
- package/lib/components/splitview/components/splitview.detail.test.js +92 -0
- package/lib/components/splitview/components/splitview.js +69 -0
- package/lib/components/splitview/components/splitview.master.js +26 -0
- package/lib/components/splitview/components/splitview.master.test.js +55 -0
- package/lib/components/splitview/components/splitview.test.js +76 -0
- package/lib/components/splitview/index.js +3 -0
- package/lib/components/translate/README.md +56 -0
- package/lib/components/translate/components/translate.js +100 -0
- package/lib/components/translate/components/translate.test.js +226 -0
- package/lib/components/translate/index.js +1 -0
- package/lib/index.js +2 -0
- package/package.json +68 -0
- package/showcase/design/.htaccess +8 -0
- package/showcase/design/core/factories/development/development.factory.js +5 -0
- package/showcase/design/core/factories/development/index.js +1 -0
- package/showcase/design/core/factories/index.js +11 -0
- package/showcase/design/core/factories/standard.factory.js +19 -0
- package/showcase/design/index.html +22 -0
- package/showcase/design/index.js +7 -0
- package/showcase/design/screens/base/audio/audioDemo.js +32 -0
- package/showcase/design/screens/base/audio/index.js +25 -0
- package/showcase/design/screens/base/camera/cameraDemo.js +83 -0
- package/showcase/design/screens/base/camera/index.js +25 -0
- package/showcase/design/screens/base/droparea/dropareaDemo.js +88 -0
- package/showcase/design/screens/base/droparea/index.js +25 -0
- package/showcase/design/screens/base/index.js +42 -0
- package/showcase/design/screens/base/list/index.js +25 -0
- package/showcase/design/screens/base/list/listDemo.js +89 -0
- package/showcase/design/screens/base/paginator/index.js +25 -0
- package/showcase/design/screens/base/paginator/paginatorDemo.js +82 -0
- package/showcase/design/screens/base/root.component.js +294 -0
- package/showcase/design/screens/base/root.routes.js +28 -0
- package/showcase/design/screens/base/spinner/index.js +25 -0
- package/showcase/design/screens/base/spinner/spinnerDemo.js +55 -0
- package/showcase/design/screens/base/splitview/detailDemo.js +40 -0
- package/showcase/design/screens/base/splitview/index.js +25 -0
- package/showcase/design/screens/base/splitview/splitViewDemo.js +58 -0
- package/showcase/design/screens/base/translate/index.js +20 -0
- package/showcase/design/screens/base/translate/translateDemo.js +43 -0
- package/showcase/design/screens/main.js +12 -0
- package/showcase/design/screens/screens.routes.js +23 -0
- package/showcase/index.html +86 -0
- package/showcase/index.js +5 -0
- package/showcase/locales/en/default.json +5 -0
- package/showcase/locales/es/default.json +5 -0
- package/showcase/locales/fr/default.json +5 -0
- package/tsconfig.json +23 -0
- package/webpack.config.cjs +118 -0
package/Makefile
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
.PHONY: all tests coverage
|
|
2
|
+
|
|
3
|
+
setup:
|
|
4
|
+
npm install -g browser-sync && \
|
|
5
|
+
npm install -g http-server && \
|
|
6
|
+
sudo apt-get install build-essential libcairo2-dev \
|
|
7
|
+
libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev
|
|
8
|
+
|
|
9
|
+
install:
|
|
10
|
+
npm install
|
|
11
|
+
|
|
12
|
+
test:
|
|
13
|
+
npm run test
|
|
14
|
+
|
|
15
|
+
clean:
|
|
16
|
+
rm -rf ./dist
|
|
17
|
+
|
|
18
|
+
showcase:
|
|
19
|
+
npx live-server ./showcase/
|
|
20
|
+
|
|
21
|
+
dev:
|
|
22
|
+
npm run dev
|
|
23
|
+
|
|
24
|
+
prod:
|
|
25
|
+
npm run prod
|
|
26
|
+
|
|
27
|
+
push:
|
|
28
|
+
git push && git push --tags
|
|
29
|
+
|
|
30
|
+
deploy:
|
|
31
|
+
sshpass -e rsync -av --delete ./dist/ \
|
|
32
|
+
${RSYNC_USER}@${RSYNC_HOST}:${RSYNC_DIR}
|
|
33
|
+
|
|
34
|
+
reset:
|
|
35
|
+
git reset --hard
|
|
36
|
+
git clean -xdf
|
|
37
|
+
|
|
38
|
+
purge:
|
|
39
|
+
rm -rf ./node_modules
|
|
40
|
+
|
|
41
|
+
http:
|
|
42
|
+
http-server -p 8084 -c-1 dist
|
|
43
|
+
|
|
44
|
+
updates:
|
|
45
|
+
npx npm-check-updates
|
|
46
|
+
|
|
47
|
+
upgrade:
|
|
48
|
+
npx npm-check-updates --upgrade
|
|
49
|
+
npm install
|
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# ComponArk
|
|
2
|
+
|
|
3
|
+
Pragmatic Web Components Library
|
|
4
|
+
|
|
5
|
+
Introduction
|
|
6
|
+
============
|
|
7
|
+
|
|
8
|
+
Componark is a collection of ready to use web components to propel the creation
|
|
9
|
+
of user interfaces. A common *Component* base class is provided to ensure
|
|
10
|
+
inter-component compatibility and common development idioms. This class is only
|
|
11
|
+
a thin wrapper on top of the *HTMLElement* custom elements class, and might be
|
|
12
|
+
extended in your own application code to create new components.
|
|
13
|
+
|
|
14
|
+
Reference
|
|
15
|
+
=========
|
|
16
|
+
|
|
17
|
+
- `Base Component <lib/base/component>`_
|
|
18
|
+
|
|
19
|
+
Components library
|
|
20
|
+
------------------
|
|
21
|
+
|
|
22
|
+
- `<ark-accordion> <lib/components/accordion>`_
|
|
23
|
+
- `<ark-alert> <lib/components/alert>`_
|
|
24
|
+
- `<ark-audio> <lib/components/audio>`_
|
|
25
|
+
- `<ark-button> <lib/components/button>`_
|
|
26
|
+
- `<ark-camera> <lib/components/camera>`_
|
|
27
|
+
- `<ark-card> <lib/components/card>`_
|
|
28
|
+
- `<ark-chart> <lib/components/chart>`_
|
|
29
|
+
- `<ark-checkbox> <lib/components/checkbox>`_
|
|
30
|
+
- `<ark-droparea> <lib/components/droparea>`_
|
|
31
|
+
- `<ark-gallery> <lib/components/gallery>`_
|
|
32
|
+
- `<ark-icon> <lib/components/icon>`_
|
|
33
|
+
- `<ark-list> <lib/components/list>`_
|
|
34
|
+
- `<ark-input> <lib/components/input>`_
|
|
35
|
+
- `<ark-location> <lib/components/location>`_
|
|
36
|
+
- `<ark-map> <lib/components/map>`_
|
|
37
|
+
- `<ark-modal> <lib/components/modal>`_
|
|
38
|
+
- `<ark-multiselect> <lib/components/multiselect>`_
|
|
39
|
+
- `<ark-paginator> <lib/components/paginator>`_
|
|
40
|
+
- `<ark-radio> <lib/components/radio>`_
|
|
41
|
+
- `<ark-select> <lib/components/select>`_
|
|
42
|
+
- `<ark-sidebar> <lib/components/sidebar>`_
|
|
43
|
+
- `<ark-signature> <lib/components/signature>`_
|
|
44
|
+
- `<ark-spinner> <lib/components/spinner>`_
|
|
45
|
+
- `<ark-splitview> <lib/components/splitview>`_
|
|
46
|
+
- `<ark-tabs> <lib/components/tabs>`_
|
|
47
|
+
- `<ark-tooltip> <lib/components/tooltip>`_
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"folders": [
|
|
3
|
+
{
|
|
4
|
+
"path": "."
|
|
5
|
+
}
|
|
6
|
+
],
|
|
7
|
+
"settings": {
|
|
8
|
+
"editor.tabSize": 2,
|
|
9
|
+
"editor.insertSpaces": true,
|
|
10
|
+
"editor.detectIndentation": false
|
|
11
|
+
},
|
|
12
|
+
"extensions": {
|
|
13
|
+
"recommendations": [
|
|
14
|
+
"bashmish.es6-string-css",
|
|
15
|
+
"tobermory.es6-string-html",
|
|
16
|
+
"zjcompt.es6-string-javascript",
|
|
17
|
+
"xabikos.javascriptsnippets",
|
|
18
|
+
"graphql.vscode-graphql",
|
|
19
|
+
"dbaeumer.vscode-eslint",
|
|
20
|
+
"syler.sass-indented",
|
|
21
|
+
"yzhang.markdown-all-in-one",
|
|
22
|
+
"mervin.markdown-formatter",
|
|
23
|
+
"shd101wyy.markdown-preview-enhanced",
|
|
24
|
+
"shuworks.vscode-table-formatter",
|
|
25
|
+
"waderyan.gitblame",
|
|
26
|
+
"snooty.snooty",
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
Component
|
|
2
|
+
*********
|
|
3
|
+
|
|
4
|
+
This is the base class for the *Componark* library of web components. It might
|
|
5
|
+
be used in application code as well as to create new custom elements, which can
|
|
6
|
+
of course use the pre-built components in this library in their composition.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
Reference
|
|
10
|
+
=========
|
|
11
|
+
|
|
12
|
+
Usage
|
|
13
|
+
-----
|
|
14
|
+
|
|
15
|
+
A new component (i.e. custom element) should be created by just extending the
|
|
16
|
+
*Component* base class.
|
|
17
|
+
|
|
18
|
+
.. code:: javascript
|
|
19
|
+
|
|
20
|
+
import { Component } from 'base/component'
|
|
21
|
+
|
|
22
|
+
class MyComponent extends Component {
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
Lifecycle
|
|
28
|
+
---------
|
|
29
|
+
|
|
30
|
+
The lifecycle of the Componark's base component overlaps with that of the
|
|
31
|
+
standard `custom elements specification <https://developer.mozilla.org/en-US/
|
|
32
|
+
docs/Web/Web_Components/Using_custom_elements#using_the_lifecycle_callbacks>`_.
|
|
33
|
+
Howerver, the *Component* base class provides additional extension points to
|
|
34
|
+
simplify state management and the rendering process.
|
|
35
|
+
|
|
36
|
+
- constructor()
|
|
37
|
+
- **init(context: object)**
|
|
38
|
+
- connectedCallback()
|
|
39
|
+
- **async update()**
|
|
40
|
+
- **render()**
|
|
41
|
+
- **async load()**
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
Methods
|
|
45
|
+
-------
|
|
46
|
+
|
|
47
|
+
constructor()
|
|
48
|
+
^^^^^^^^^^^^^
|
|
49
|
+
|
|
50
|
+
This is a custom element *standard* method. The constructor *must not*
|
|
51
|
+
receive any parameters and is invoked every time a new instance of a
|
|
52
|
+
*Component* is **created**. The default constructor implementation calls the
|
|
53
|
+
**init()** method with an empty object and that is the place in which custom
|
|
54
|
+
initialization of state should be performed when using componark. The automatic
|
|
55
|
+
syncronization between reflected properties and attributes is also done inside
|
|
56
|
+
the constructor.
|
|
57
|
+
|
|
58
|
+
init(context: object)
|
|
59
|
+
^^^^^^^^^^^^^^^^^^^^^
|
|
60
|
+
|
|
61
|
+
The **init()** method is the place where state initialization of the component
|
|
62
|
+
should be made. It is called without context arguments by the constructor on
|
|
63
|
+
component creation and thus, should resiliently handle any form of attributes
|
|
64
|
+
received to set the state of the component before **rendering**. The *init()*
|
|
65
|
+
method should be called as well in parent components, for *updating* the
|
|
66
|
+
state of the nested component and then explicitly invoking **render()** to
|
|
67
|
+
present those changes in the user interface. The **init()** method returns the
|
|
68
|
+
current instance (i.e. *this*) to enable chaining, so don't forget to return
|
|
69
|
+
*super.init()* when extending this method.
|
|
70
|
+
|
|
71
|
+
connectedCallback()
|
|
72
|
+
^^^^^^^^^^^^^^^^^^^
|
|
73
|
+
|
|
74
|
+
The *connectedCallback()* method is one of the default lifecycle callbacks or
|
|
75
|
+
reactions of the `custom elements specification
|
|
76
|
+
<https://developer.mozilla.org/en-US/docs/Web/Web_Components/
|
|
77
|
+
Using_custom_elements#using_the_lifecycle_callbacks>`_. This method shouldn't
|
|
78
|
+
be extended directly by Componark's components as there are other methods in
|
|
79
|
+
this reference which are better suited to provide customization and extension.
|
|
80
|
+
The **connectedCallback()** is invoked every time a *Component* is inserted
|
|
81
|
+
into the *DOM* (i.e. injected in the page).
|
|
82
|
+
|
|
83
|
+
async update()
|
|
84
|
+
^^^^^^^^^^^^^^
|
|
85
|
+
|
|
86
|
+
The *update()* method is an *asynchronous* consolidation method whose purpose
|
|
87
|
+
is to simultaneously call **render()** and **load()**. The *update()* method is
|
|
88
|
+
called by the *connectedCallback()* standard reaction and after first injection
|
|
89
|
+
might also be called explicitly by user code.
|
|
90
|
+
|
|
91
|
+
render()
|
|
92
|
+
^^^^^^^^
|
|
93
|
+
|
|
94
|
+
This method should be extended by every component to set its visual
|
|
95
|
+
representation (i.e. html) based on its current state. The produced document
|
|
96
|
+
string should be assigned to the **content** property of the *Component*
|
|
97
|
+
which in turn will inject it into the *DOM*. Conditionals, loops and any kind
|
|
98
|
+
of logic can be used inside the *render()* method to dynamically set the
|
|
99
|
+
*Components* presentation based on its attributes, properties or any other
|
|
100
|
+
environmental state. The **render()** method returns the current instance
|
|
101
|
+
(i.e. *this*) to enable chaining, so don't forget to return *super.render()*
|
|
102
|
+
when extending this method.
|
|
103
|
+
|
|
104
|
+
async load()
|
|
105
|
+
^^^^^^^^^^^^
|
|
106
|
+
|
|
107
|
+
The *load()* lifecycle asynchronous method is the place where custom data
|
|
108
|
+
fetching or other kinds of asynchronous operations should be performed. This
|
|
109
|
+
method is invoked by *connectedCallback()* every time the *Component* is
|
|
110
|
+
injected into the DOM (in an indirect manner through *update()*). After
|
|
111
|
+
finishing its asynchronous tasks (e.g. obtaining some requested external data)
|
|
112
|
+
the *load()* method might change the component's state and presentation and
|
|
113
|
+
even explicitly call *render()*.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { define, listen, reflect, slot, keys } from "../utils/index.js"
|
|
2
|
+
import styles from '../styles/index.js'
|
|
3
|
+
|
|
4
|
+
const tag = 'ark-component'
|
|
5
|
+
export class Component extends HTMLElement {
|
|
6
|
+
constructor() {
|
|
7
|
+
super()
|
|
8
|
+
this.binding = "listen"
|
|
9
|
+
reflect(this, this.reflectedProperties())
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {string} tag
|
|
14
|
+
* @param {CustomElementConstructor} element
|
|
15
|
+
* @param {string} styles **/
|
|
16
|
+
static define(tag, element, styles = null) {
|
|
17
|
+
define(tag, element, styles)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {object} styleMap
|
|
22
|
+
* @return {string} **/
|
|
23
|
+
styleNames(styleMap) {
|
|
24
|
+
return keys(styleMap)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {Object<string, any>} _context
|
|
29
|
+
* @return {Component} */
|
|
30
|
+
init(_context = {}) {
|
|
31
|
+
return this
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** @return {string[]} */
|
|
35
|
+
reflectedProperties() {
|
|
36
|
+
return []
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get slots() {
|
|
40
|
+
return slot(this)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** @param {string} content */
|
|
44
|
+
set content(content) {
|
|
45
|
+
this.innerHTML = content
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** @return {string} */
|
|
49
|
+
get content() {
|
|
50
|
+
return this.innerHTML
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
connectedCallback() {
|
|
54
|
+
try {
|
|
55
|
+
!this['state'] && this.init({})
|
|
56
|
+
this.render()
|
|
57
|
+
} catch (error) {
|
|
58
|
+
this.emit("error", error)
|
|
59
|
+
throw error
|
|
60
|
+
}
|
|
61
|
+
this.load()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** @return {Component} */
|
|
65
|
+
render() {
|
|
66
|
+
this.classList.add(this.tagName.toLowerCase())
|
|
67
|
+
listen(this)
|
|
68
|
+
return this
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async load() {}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {string} selectors
|
|
75
|
+
* @return {Component} */
|
|
76
|
+
select(selectors) {
|
|
77
|
+
return /** @type {Component} */ (this.querySelector(selectors))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param {string} selectors
|
|
82
|
+
* @return {NodeListOf<Component>} */
|
|
83
|
+
selectAll(selectors) {
|
|
84
|
+
return /** @type {NodeListOf<Component>} */ (this.querySelectorAll(
|
|
85
|
+
selectors
|
|
86
|
+
))
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param {string} type
|
|
91
|
+
* @param {any} detail */
|
|
92
|
+
emit(type, detail) {
|
|
93
|
+
this.dispatchEvent(
|
|
94
|
+
new CustomEvent(type, {
|
|
95
|
+
detail,
|
|
96
|
+
bubbles: true,
|
|
97
|
+
cancelable: true,
|
|
98
|
+
})
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @param {string} resource
|
|
104
|
+
* @return {any} */
|
|
105
|
+
resolve(resource) {
|
|
106
|
+
const event = new CustomEvent("resolve", {
|
|
107
|
+
detail: { resource },
|
|
108
|
+
bubbles: true,
|
|
109
|
+
cancelable: true,
|
|
110
|
+
})
|
|
111
|
+
this.dispatchEvent(event)
|
|
112
|
+
return event.detail[resource]
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
Component.define(tag, Component, styles)
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { jest } from '@jest/globals'
|
|
2
|
+
import { Component } from './component.js'
|
|
3
|
+
|
|
4
|
+
class MockComponent extends Component {
|
|
5
|
+
init(context = {}) {
|
|
6
|
+
this.context = context
|
|
7
|
+
this.data = {}
|
|
8
|
+
this.dependency = undefined
|
|
9
|
+
return super.init()
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
reflectedProperties() { return ['code'] }
|
|
13
|
+
|
|
14
|
+
erroringHandler (_event) {
|
|
15
|
+
throw new Error('Something went wrong!')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async asyncErroringHandler (_event) {
|
|
19
|
+
const callback = async () => {
|
|
20
|
+
throw new Error('Something went async wrong!') }
|
|
21
|
+
await callback()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
render() {
|
|
25
|
+
this.dependency = this.resolve('Dependency')
|
|
26
|
+
return super.render()
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
Component.define('mock-component', MockComponent)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
describe('Component', () => {
|
|
33
|
+
let container = null
|
|
34
|
+
let component = null
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
container = document.createElement('div')
|
|
37
|
+
container.innerHTML = `<mock-component code="XYZ123"></mock-component>`
|
|
38
|
+
component = container.querySelector('mock-component')
|
|
39
|
+
document.body.append(container)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
afterEach(() => {
|
|
43
|
+
container.remove()
|
|
44
|
+
container = null
|
|
45
|
+
component = null
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('can be instantiated', () => {
|
|
49
|
+
expect(component).toBeTruthy()
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('has an stable public api', () => {
|
|
53
|
+
expect(typeof component.constructor.define).toEqual('function')
|
|
54
|
+
expect(typeof component.init).toEqual('function')
|
|
55
|
+
expect(typeof component.reflectedProperties).toEqual('function')
|
|
56
|
+
expect(typeof component.connectedCallback).toEqual('function')
|
|
57
|
+
expect(typeof component.render).toEqual('function')
|
|
58
|
+
expect(typeof component.load).toEqual('function')
|
|
59
|
+
expect(typeof component.select).toEqual('function')
|
|
60
|
+
expect(typeof component.selectAll).toEqual('function')
|
|
61
|
+
expect(typeof component.emit).toEqual('function')
|
|
62
|
+
expect(typeof component.content).toBeDefined()
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('has an init method through which state is set', () => {
|
|
66
|
+
const response = component.init({attribute: 'value'})
|
|
67
|
+
expect(response).toBe(component)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('can set its content via a property', () => {
|
|
71
|
+
component.content = `<p>Hello World</p>`
|
|
72
|
+
const paragraph = component.querySelector('p')
|
|
73
|
+
expect(component.content).toBe(`<p>Hello World</p>`)
|
|
74
|
+
expect(paragraph.outerHTML).toBe(`<p>Hello World</p>`)
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('can have some of its attributes reflected as properties', () => {
|
|
78
|
+
expect(component.code).toBe('XYZ123')
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('sets its tag name as class when rendered', () => {
|
|
82
|
+
component.render()
|
|
83
|
+
expect(component.className).toEqual('mock-component')
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('keeps its previous classes after rendering', () => {
|
|
87
|
+
component.classList.add('custom-class')
|
|
88
|
+
component.classList.add('custom-class')
|
|
89
|
+
component.classList.add('special-class')
|
|
90
|
+
component.render()
|
|
91
|
+
expect(component.className).toEqual(
|
|
92
|
+
'mock-component custom-class special-class')
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('emits custom events', () => {
|
|
96
|
+
let detail = null
|
|
97
|
+
const handler = (event) => {detail = event.detail}
|
|
98
|
+
component.addEventListener('fire', handler)
|
|
99
|
+
|
|
100
|
+
component.emit('fire', {location: 'indoors'})
|
|
101
|
+
|
|
102
|
+
expect(detail).toEqual({location: 'indoors'})
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('calls the load method on connectedCallback', async () => {
|
|
106
|
+
const component = document.createElement('mock-component')
|
|
107
|
+
const initSpy = jest.spyOn(component, 'init')
|
|
108
|
+
const renderSpy = jest.spyOn(component, 'render')
|
|
109
|
+
const loadSpy = jest.spyOn(component, 'load')
|
|
110
|
+
|
|
111
|
+
document.body.append(component)
|
|
112
|
+
|
|
113
|
+
expect(initSpy).toHaveBeenCalledTimes(1)
|
|
114
|
+
expect(renderSpy).toHaveBeenCalledTimes(1)
|
|
115
|
+
expect(loadSpy).toHaveBeenCalledTimes(1)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('catches and re-raises connectedCallback errors', async () => {
|
|
119
|
+
expect.assertions(1)
|
|
120
|
+
const component = document.createElement('mock-component')
|
|
121
|
+
const consoleError = console.error
|
|
122
|
+
console.error = jest.fn()
|
|
123
|
+
component.render = () => {
|
|
124
|
+
throw new Error('Render Error!')
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
component.connectedCallback()
|
|
129
|
+
} catch (error) {
|
|
130
|
+
expect(error.message).toEqual('Render Error!')
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
console.error = consoleError
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
it('selects the children matching a selector', () => {
|
|
138
|
+
container.innerHTML = `
|
|
139
|
+
<mock-component>
|
|
140
|
+
<div class="blue"></div>
|
|
141
|
+
<div class="red"></div>
|
|
142
|
+
<div class="red"></div>
|
|
143
|
+
</mock-component>
|
|
144
|
+
`
|
|
145
|
+
const component = container.querySelector('mock-component')
|
|
146
|
+
|
|
147
|
+
const blue = component.select('.blue')
|
|
148
|
+
expect(blue.tagName).toEqual('DIV')
|
|
149
|
+
|
|
150
|
+
const red = component.selectAll('.red')
|
|
151
|
+
expect(red.length).toEqual(2)
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
it('retrieves its slots through the slots method', () => {
|
|
155
|
+
container.innerHTML = `
|
|
156
|
+
<mock-component>
|
|
157
|
+
<div slot="header" class="header"></div>
|
|
158
|
+
<div class="body"></div>
|
|
159
|
+
<div class="aside"></div>
|
|
160
|
+
<div slot="footer" class="footer"></div>
|
|
161
|
+
</mock-component>
|
|
162
|
+
`
|
|
163
|
+
const component = container.querySelector('mock-component')
|
|
164
|
+
|
|
165
|
+
expect(component.slots).toEqual({
|
|
166
|
+
header: [component.select('.header')],
|
|
167
|
+
general: [component.select('.body'), component.select('.aside')],
|
|
168
|
+
footer: [component.select('.footer')]
|
|
169
|
+
})
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('binds its properties to children events', async () => {
|
|
173
|
+
container.innerHTML = `
|
|
174
|
+
<mock-component>
|
|
175
|
+
<input type="text" listen on-input="{{ data.value = data }}"></input>
|
|
176
|
+
</mock-component>
|
|
177
|
+
`
|
|
178
|
+
|
|
179
|
+
const component = container.querySelector('mock-component')
|
|
180
|
+
const input = component.select('input')
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
input.dispatchEvent(new InputEvent('input', {bubbles: true, data: 'E'}))
|
|
184
|
+
|
|
185
|
+
expect(component.data.value).toEqual('E')
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('binds to the target.value event property by default', async () => {
|
|
189
|
+
container.innerHTML = `
|
|
190
|
+
<mock-component>
|
|
191
|
+
<input type="text" listen on-input="{{ data.value }}"></input>
|
|
192
|
+
</mock-component>
|
|
193
|
+
`
|
|
194
|
+
const component = container.querySelector('mock-component')
|
|
195
|
+
const input = component.select('input')
|
|
196
|
+
const inputEvent = new InputEvent('input')
|
|
197
|
+
const target = { name: 'input', value: 'X' }
|
|
198
|
+
Object.defineProperty(
|
|
199
|
+
inputEvent, 'target', { writable: false, value: target });
|
|
200
|
+
|
|
201
|
+
input.dispatchEvent(inputEvent)
|
|
202
|
+
|
|
203
|
+
expect(component.data.value).toEqual('X')
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
it('binds to the detail custom event property', async () => {
|
|
207
|
+
container.innerHTML = `
|
|
208
|
+
<mock-component>
|
|
209
|
+
<input type="text" listen on-alter="{{ data.value }}"></input>
|
|
210
|
+
</mock-component>
|
|
211
|
+
`
|
|
212
|
+
|
|
213
|
+
const component = container.querySelector('mock-component')
|
|
214
|
+
const input = component.select('input')
|
|
215
|
+
|
|
216
|
+
input.dispatchEvent(
|
|
217
|
+
new CustomEvent('alter', { bubbles: true, detail: 'A' }))
|
|
218
|
+
|
|
219
|
+
expect(component.data.value).toEqual('A')
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
it('emits an error event on declared listeners', async () => {
|
|
223
|
+
container.innerHTML = `
|
|
224
|
+
<mock-component>
|
|
225
|
+
<input type="text" listen on-alter="erroringHandler"></input>
|
|
226
|
+
</mock-component>
|
|
227
|
+
`
|
|
228
|
+
|
|
229
|
+
const component = container.querySelector('mock-component')
|
|
230
|
+
|
|
231
|
+
let errorEvent = {}
|
|
232
|
+
component.addEventListener('error', (event) => errorEvent = event)
|
|
233
|
+
|
|
234
|
+
const input = component.select('input')
|
|
235
|
+
|
|
236
|
+
input.dispatchEvent(
|
|
237
|
+
new CustomEvent('alter', { bubbles: true, detail: 'I will error!' }))
|
|
238
|
+
|
|
239
|
+
expect(errorEvent.detail.message).toEqual('Something went wrong!')
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
it('emits an error event on declared async listeners', async () => {
|
|
243
|
+
container.innerHTML = `
|
|
244
|
+
<mock-component>
|
|
245
|
+
<input type="text" listen on-alter="asyncErroringHandler"></input>
|
|
246
|
+
</mock-component>
|
|
247
|
+
`
|
|
248
|
+
|
|
249
|
+
const component = container.querySelector('mock-component')
|
|
250
|
+
|
|
251
|
+
let errorEvent = {}
|
|
252
|
+
component.addEventListener('error', (event) => errorEvent = event)
|
|
253
|
+
|
|
254
|
+
const input = component.select('input')
|
|
255
|
+
|
|
256
|
+
input.dispatchEvent(
|
|
257
|
+
new CustomEvent('alter', { bubbles: true, detail: 'I will error!' }))
|
|
258
|
+
|
|
259
|
+
// Sleep
|
|
260
|
+
await new Promise(resolve => setTimeout(resolve, 0))
|
|
261
|
+
|
|
262
|
+
expect(errorEvent.detail.message).toEqual('Something went async wrong!')
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
it('resolves its resource dependencies using events propagation', () => {
|
|
266
|
+
const listener = (event) => {
|
|
267
|
+
const resource = event.detail.resource
|
|
268
|
+
event.detail[resource] = 'RESOLVED_DEPENDENCY'
|
|
269
|
+
}
|
|
270
|
+
document.addEventListener('resolve', listener)
|
|
271
|
+
container.innerHTML = `
|
|
272
|
+
<mock-component></mock-component>
|
|
273
|
+
`
|
|
274
|
+
const component = container.querySelector('mock-component')
|
|
275
|
+
|
|
276
|
+
expect(component.dependency).toEqual('RESOLVED_DEPENDENCY')
|
|
277
|
+
document.removeEventListener('resolve', listener)
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
it('provides the dependencies requested to it by child components', () => {
|
|
281
|
+
class ParentComponent extends MockComponent {
|
|
282
|
+
provide(resource) {
|
|
283
|
+
if (resource === 'Dependency') {
|
|
284
|
+
return `RESOURCE: ${resource} PROVIDED BY: ${this.id}`
|
|
285
|
+
}
|
|
286
|
+
if (resource === 'state') return { key: 'value' }
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
Component.define('parent-component', ParentComponent)
|
|
290
|
+
|
|
291
|
+
container.innerHTML = `
|
|
292
|
+
<parent-component id="parent">
|
|
293
|
+
<mock-component id="child"></mock-component>
|
|
294
|
+
</parent-component>
|
|
295
|
+
`
|
|
296
|
+
const parent = container.querySelector('#parent')
|
|
297
|
+
const child = container.querySelector('#child')
|
|
298
|
+
|
|
299
|
+
expect(child.dependency).toEqual(
|
|
300
|
+
"RESOURCE: Dependency PROVIDED BY: parent")
|
|
301
|
+
|
|
302
|
+
const state = child.resolve('state')
|
|
303
|
+
expect(state).toEqual({ key: 'value' })
|
|
304
|
+
|
|
305
|
+
const unknown = child.resolve('unknown')
|
|
306
|
+
expect(unknown).toBe(undefined)
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
it('provides a styleNames utility function for setting styles', () => {
|
|
310
|
+
container.innerHTML = `
|
|
311
|
+
<mock-component class></mock-component>
|
|
312
|
+
`
|
|
313
|
+
const component = container.querySelector('mock-component')
|
|
314
|
+
const background = 'primary'
|
|
315
|
+
const shadow = 'small'
|
|
316
|
+
const color = ''
|
|
317
|
+
const styleMap = {
|
|
318
|
+
[`background-${background}`]: background,
|
|
319
|
+
[`color-${color}`]: color,
|
|
320
|
+
[`shadow-${shadow}`]: shadow,
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const result = component.styleNames(styleMap)
|
|
324
|
+
|
|
325
|
+
expect(result).toEqual('background-primary shadow-small')
|
|
326
|
+
})
|
|
327
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Component, css, html } from './component/index.js'
|