@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.
Files changed (116) hide show
  1. package/Makefile +49 -0
  2. package/README.md +47 -0
  3. package/knowarkjs.code-workspace +29 -0
  4. package/lib/base/component/README.rst +113 -0
  5. package/lib/base/component/component.js +115 -0
  6. package/lib/base/component/component.test.js +327 -0
  7. package/lib/base/component/index.js +3 -0
  8. package/lib/base/index.js +1 -0
  9. package/lib/base/styles/index.js +3 -0
  10. package/lib/base/styles/styles.js +320 -0
  11. package/lib/base/utils/define.js +21 -0
  12. package/lib/base/utils/define.test.js +62 -0
  13. package/lib/base/utils/format.js +24 -0
  14. package/lib/base/utils/format.test.js +19 -0
  15. package/lib/base/utils/helpers.js +96 -0
  16. package/lib/base/utils/helpers.test.js +154 -0
  17. package/lib/base/utils/index.js +5 -0
  18. package/lib/base/utils/slots.js +18 -0
  19. package/lib/base/utils/slots.test.js +52 -0
  20. package/lib/base/utils/uuid.js +9 -0
  21. package/lib/base/utils/uuid.test.js +19 -0
  22. package/lib/components/audio/README.md +22 -0
  23. package/lib/components/audio/components/audio.js +103 -0
  24. package/lib/components/audio/components/audio.test.js +127 -0
  25. package/lib/components/audio/index.js +1 -0
  26. package/lib/components/audio/styles/ark.css.js +83 -0
  27. package/lib/components/audio/styles/index.js +2 -0
  28. package/lib/components/camera/README.md +64 -0
  29. package/lib/components/camera/components/camera.js +85 -0
  30. package/lib/components/camera/components/camera.test.js +104 -0
  31. package/lib/components/camera/index.js +1 -0
  32. package/lib/components/camera/styles/ark.css.js +17 -0
  33. package/lib/components/camera/styles/index.js +2 -0
  34. package/lib/components/capture/components/capture.js +54 -0
  35. package/lib/components/capture/components/capture.test.js +112 -0
  36. package/lib/components/capture/index.js +1 -0
  37. package/lib/components/droparea/README.md +51 -0
  38. package/lib/components/droparea/components/droparea-preview.js +159 -0
  39. package/lib/components/droparea/components/droparea-preview.test.js +105 -0
  40. package/lib/components/droparea/components/droparea.js +165 -0
  41. package/lib/components/droparea/components/droparea.test.js +320 -0
  42. package/lib/components/droparea/index.js +1 -0
  43. package/lib/components/droparea/styles/ark.css.js +235 -0
  44. package/lib/components/droparea/styles/index.js +3 -0
  45. package/lib/components/emit/components/emit.js +33 -0
  46. package/lib/components/emit/components/emit.test.js +138 -0
  47. package/lib/components/emit/index.js +1 -0
  48. package/lib/components/index.js +9 -0
  49. package/lib/components/list/README.md +103 -0
  50. package/lib/components/list/components/item.test.js +93 -0
  51. package/lib/components/list/components/list.item.js +22 -0
  52. package/lib/components/list/components/list.js +96 -0
  53. package/lib/components/list/components/list.test.js +267 -0
  54. package/lib/components/list/index.js +2 -0
  55. package/lib/components/paginator/README.md +32 -0
  56. package/lib/components/paginator/components/paginator.js +110 -0
  57. package/lib/components/paginator/components/paginator.test.js +131 -0
  58. package/lib/components/paginator/index.js +1 -0
  59. package/lib/components/paginator/styles/ark.css.js +196 -0
  60. package/lib/components/paginator/styles/index.js +2 -0
  61. package/lib/components/spinner/README.md +41 -0
  62. package/lib/components/spinner/components/spinner.js +105 -0
  63. package/lib/components/spinner/components/spinner.test.js +50 -0
  64. package/lib/components/spinner/index.js +1 -0
  65. package/lib/components/spinner/styles/ark.css.js +658 -0
  66. package/lib/components/spinner/styles/index.js +2 -0
  67. package/lib/components/splitview/README.md +63 -0
  68. package/lib/components/splitview/components/splitview.detail.js +46 -0
  69. package/lib/components/splitview/components/splitview.detail.test.js +92 -0
  70. package/lib/components/splitview/components/splitview.js +69 -0
  71. package/lib/components/splitview/components/splitview.master.js +26 -0
  72. package/lib/components/splitview/components/splitview.master.test.js +55 -0
  73. package/lib/components/splitview/components/splitview.test.js +76 -0
  74. package/lib/components/splitview/index.js +3 -0
  75. package/lib/components/translate/README.md +56 -0
  76. package/lib/components/translate/components/translate.js +100 -0
  77. package/lib/components/translate/components/translate.test.js +226 -0
  78. package/lib/components/translate/index.js +1 -0
  79. package/lib/index.js +2 -0
  80. package/package.json +68 -0
  81. package/showcase/design/.htaccess +8 -0
  82. package/showcase/design/core/factories/development/development.factory.js +5 -0
  83. package/showcase/design/core/factories/development/index.js +1 -0
  84. package/showcase/design/core/factories/index.js +11 -0
  85. package/showcase/design/core/factories/standard.factory.js +19 -0
  86. package/showcase/design/index.html +22 -0
  87. package/showcase/design/index.js +7 -0
  88. package/showcase/design/screens/base/audio/audioDemo.js +32 -0
  89. package/showcase/design/screens/base/audio/index.js +25 -0
  90. package/showcase/design/screens/base/camera/cameraDemo.js +83 -0
  91. package/showcase/design/screens/base/camera/index.js +25 -0
  92. package/showcase/design/screens/base/droparea/dropareaDemo.js +88 -0
  93. package/showcase/design/screens/base/droparea/index.js +25 -0
  94. package/showcase/design/screens/base/index.js +42 -0
  95. package/showcase/design/screens/base/list/index.js +25 -0
  96. package/showcase/design/screens/base/list/listDemo.js +89 -0
  97. package/showcase/design/screens/base/paginator/index.js +25 -0
  98. package/showcase/design/screens/base/paginator/paginatorDemo.js +82 -0
  99. package/showcase/design/screens/base/root.component.js +294 -0
  100. package/showcase/design/screens/base/root.routes.js +28 -0
  101. package/showcase/design/screens/base/spinner/index.js +25 -0
  102. package/showcase/design/screens/base/spinner/spinnerDemo.js +55 -0
  103. package/showcase/design/screens/base/splitview/detailDemo.js +40 -0
  104. package/showcase/design/screens/base/splitview/index.js +25 -0
  105. package/showcase/design/screens/base/splitview/splitViewDemo.js +58 -0
  106. package/showcase/design/screens/base/translate/index.js +20 -0
  107. package/showcase/design/screens/base/translate/translateDemo.js +43 -0
  108. package/showcase/design/screens/main.js +12 -0
  109. package/showcase/design/screens/screens.routes.js +23 -0
  110. package/showcase/index.html +86 -0
  111. package/showcase/index.js +5 -0
  112. package/showcase/locales/en/default.json +5 -0
  113. package/showcase/locales/es/default.json +5 -0
  114. package/showcase/locales/fr/default.json +5 -0
  115. package/tsconfig.json +23 -0
  116. 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,3 @@
1
+ export { Component } from './component.js'
2
+ export const css = String.raw
3
+ export const html = String.raw
@@ -0,0 +1 @@
1
+ export { Component, css, html } from './component/index.js'
@@ -0,0 +1,3 @@
1
+ import styles from './styles.js'
2
+
3
+ export default styles