@brandsync/wc 0.0.1
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/LICENSE +21 -0
- package/custom-elements.json +1138 -0
- package/dist/brandsync-wc/brandsync-wc.css +1730 -0
- package/dist/brandsync-wc/brandsync-wc.esm.js +1 -0
- package/dist/brandsync-wc/brandsync-wc.esm.js.map +1 -0
- package/dist/brandsync-wc/index-Cqxk7LcP.js.map +1 -0
- package/dist/brandsync-wc/index.esm.js +1 -0
- package/dist/brandsync-wc/index.esm.js.map +1 -0
- package/dist/brandsync-wc/p-042ad15a.entry.js +1 -0
- package/dist/brandsync-wc/p-2c4df28d.entry.js +1 -0
- package/dist/brandsync-wc/p-3134f5f5.entry.js +1 -0
- package/dist/brandsync-wc/p-5739acaf.entry.js +1 -0
- package/dist/brandsync-wc/p-BOj_41o9.js +3 -0
- package/dist/brandsync-wc/p-c2cc0d6f.entry.js +1 -0
- package/dist/brandsync-wc/p-da11e421.entry.js +1 -0
- package/dist/brandsync-wc/p-db3934d2.entry.js +1 -0
- package/dist/brandsync-wc/p-e38a9744.entry.js +1 -0
- package/dist/cjs/brandsync-wc.cjs.js +24 -0
- package/dist/cjs/bs-badge.cjs.entry.js +19 -0
- package/dist/cjs/bs-button-skeleton.cjs.entry.js +19 -0
- package/dist/cjs/bs-button.cjs.entry.js +88 -0
- package/dist/cjs/bs-card.cjs.entry.js +20 -0
- package/dist/cjs/bs-composer.cjs.entry.js +116 -0
- package/dist/cjs/bs-data-table.cjs.entry.js +62 -0
- package/dist/cjs/bs-input.cjs.entry.js +42 -0
- package/dist/cjs/bs-modal.cjs.entry.js +69 -0
- package/dist/cjs/index-nyOcuVbF.js +1710 -0
- package/dist/cjs/index.cjs.js +7 -0
- package/dist/cjs/loader.cjs.js +12 -0
- package/dist/collection/collection-manifest.json +20 -0
- package/dist/collection/components/bs-badge/bs-badge.cmp.test.js +30 -0
- package/dist/collection/components/bs-badge/bs-badge.css +55 -0
- package/dist/collection/components/bs-badge/bs-badge.js +64 -0
- package/dist/collection/components/bs-badge/bs-badge.stories.js +75 -0
- package/dist/collection/components/bs-button/bs-button.cmp.test.js +81 -0
- package/dist/collection/components/bs-button/bs-button.css +247 -0
- package/dist/collection/components/bs-button/bs-button.js +242 -0
- package/dist/collection/components/bs-button/bs-button.stories.js +145 -0
- package/dist/collection/components/bs-button-skeleton/bs-button-skeleton.cmp.test.js +18 -0
- package/dist/collection/components/bs-button-skeleton/bs-button-skeleton.css +46 -0
- package/dist/collection/components/bs-button-skeleton/bs-button-skeleton.js +73 -0
- package/dist/collection/components/bs-button-skeleton/bs-button-skeleton.stories.js +25 -0
- package/dist/collection/components/bs-card/bs-card.cmp.test.js +42 -0
- package/dist/collection/components/bs-card/bs-card.css +31 -0
- package/dist/collection/components/bs-card/bs-card.js +70 -0
- package/dist/collection/components/bs-card/bs-card.stories.js +27 -0
- package/dist/collection/components/bs-composer/bs-composer.cmp.test.js +132 -0
- package/dist/collection/components/bs-composer/bs-composer.css +185 -0
- package/dist/collection/components/bs-composer/bs-composer.js +353 -0
- package/dist/collection/components/bs-composer/bs-composer.stories.js +66 -0
- package/dist/collection/components/bs-data-table/bs-data-table.css +56 -0
- package/dist/collection/components/bs-data-table/bs-data-table.js +259 -0
- package/dist/collection/components/bs-data-table/bs-data-table.stories.js +55 -0
- package/dist/collection/components/bs-input/bs-input.cmp.test.js +101 -0
- package/dist/collection/components/bs-input/bs-input.css +64 -0
- package/dist/collection/components/bs-input/bs-input.js +230 -0
- package/dist/collection/components/bs-input/bs-input.stories.js +40 -0
- package/dist/collection/components/bs-modal/bs-modal.cmp.test.js +112 -0
- package/dist/collection/components/bs-modal/bs-modal.css +66 -0
- package/dist/collection/components/bs-modal/bs-modal.js +198 -0
- package/dist/collection/components/bs-modal/bs-modal.stories.js +38 -0
- package/dist/collection/index.js +10 -0
- package/dist/collection/stories-utils.js +25 -0
- package/dist/collection/utils/utils.js +3 -0
- package/dist/collection/utils/utils.unit.test.js +16 -0
- package/dist/components/bs-badge.d.ts +11 -0
- package/dist/components/bs-badge.js +1 -0
- package/dist/components/bs-button-skeleton.d.ts +11 -0
- package/dist/components/bs-button-skeleton.js +1 -0
- package/dist/components/bs-button.d.ts +11 -0
- package/dist/components/bs-button.js +1 -0
- package/dist/components/bs-card.d.ts +11 -0
- package/dist/components/bs-card.js +1 -0
- package/dist/components/bs-composer.d.ts +11 -0
- package/dist/components/bs-composer.js +1 -0
- package/dist/components/bs-data-table.d.ts +11 -0
- package/dist/components/bs-data-table.js +1 -0
- package/dist/components/bs-input.d.ts +11 -0
- package/dist/components/bs-input.js +1 -0
- package/dist/components/bs-modal.d.ts +11 -0
- package/dist/components/bs-modal.js +1 -0
- package/dist/components/index.d.ts +35 -0
- package/dist/components/index.js +1 -0
- package/dist/components/p-Cu4gTknX.js +1 -0
- package/dist/esm/brandsync-wc.js +20 -0
- package/dist/esm/bs-badge.entry.js +17 -0
- package/dist/esm/bs-button-skeleton.entry.js +17 -0
- package/dist/esm/bs-button.entry.js +86 -0
- package/dist/esm/bs-card.entry.js +18 -0
- package/dist/esm/bs-composer.entry.js +114 -0
- package/dist/esm/bs-data-table.entry.js +60 -0
- package/dist/esm/bs-input.entry.js +40 -0
- package/dist/esm/bs-modal.entry.js +67 -0
- package/dist/esm/index-BOj_41o9.js +1701 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/loader.js +10 -0
- package/dist/index.cjs.js +1 -0
- package/dist/index.js +1 -0
- package/dist/types/components/bs-badge/bs-badge.cmp.test.d.ts +1 -0
- package/dist/types/components/bs-badge/bs-badge.d.ts +20 -0
- package/dist/types/components/bs-badge/bs-badge.stories.d.ts +19 -0
- package/dist/types/components/bs-button/bs-button.cmp.test.d.ts +1 -0
- package/dist/types/components/bs-button/bs-button.d.ts +84 -0
- package/dist/types/components/bs-button/bs-button.stories.d.ts +29 -0
- package/dist/types/components/bs-button-skeleton/bs-button-skeleton.cmp.test.d.ts +1 -0
- package/dist/types/components/bs-button-skeleton/bs-button-skeleton.d.ts +28 -0
- package/dist/types/components/bs-button-skeleton/bs-button-skeleton.stories.d.ts +10 -0
- package/dist/types/components/bs-card/bs-card.cmp.test.d.ts +1 -0
- package/dist/types/components/bs-card/bs-card.d.ts +26 -0
- package/dist/types/components/bs-card/bs-card.stories.d.ts +10 -0
- package/dist/types/components/bs-composer/bs-composer.cmp.test.d.ts +1 -0
- package/dist/types/components/bs-composer/bs-composer.d.ts +83 -0
- package/dist/types/components/bs-composer/bs-composer.stories.d.ts +17 -0
- package/dist/types/components/bs-data-table/bs-data-table.d.ts +55 -0
- package/dist/types/components/bs-data-table/bs-data-table.stories.d.ts +7 -0
- package/dist/types/components/bs-input/bs-input.cmp.test.d.ts +1 -0
- package/dist/types/components/bs-input/bs-input.d.ts +33 -0
- package/dist/types/components/bs-input/bs-input.stories.d.ts +15 -0
- package/dist/types/components/bs-modal/bs-modal.cmp.test.d.ts +1 -0
- package/dist/types/components/bs-modal/bs-modal.d.ts +45 -0
- package/dist/types/components/bs-modal/bs-modal.stories.d.ts +9 -0
- package/dist/types/components.d.ts +962 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/stencil-public-runtime.d.ts +1872 -0
- package/dist/types/stories-utils.d.ts +8 -0
- package/dist/types/utils/utils.d.ts +1 -0
- package/dist/types/utils/utils.unit.test.d.ts +1 -0
- package/loader/cdn.js +1 -0
- package/loader/index.cjs.js +1 -0
- package/loader/index.d.ts +24 -0
- package/loader/index.es2017.js +1 -0
- package/loader/index.js +2 -0
- package/package.json +67 -0
- package/readme.md +48 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { h } from "@stencil/core";
|
|
2
|
+
/**
|
|
3
|
+
* A sortable, optionally row-selectable table for tabular data.
|
|
4
|
+
*
|
|
5
|
+
* `columns`, `rows`, and `cellRenderer` are JS-property-only — HTML attributes can only carry
|
|
6
|
+
* strings, so arrays/objects/functions must be set via `element.rows = [...]` etc., not as
|
|
7
|
+
* attributes. See CONVENTIONS.md.
|
|
8
|
+
*
|
|
9
|
+
* ## When to use
|
|
10
|
+
* - Comparing structured records across the same set of fields (a list of people, bookings, etc.).
|
|
11
|
+
*
|
|
12
|
+
* ## When not to use
|
|
13
|
+
* - A handful of unrelated key/value pairs — a simple list or card is lighter-weight.
|
|
14
|
+
* - Deeply nested/hierarchical data — this component renders one flat row per record.
|
|
15
|
+
*
|
|
16
|
+
* @part table - The `<table>` element.
|
|
17
|
+
* @part head - The `<thead>` element.
|
|
18
|
+
* @part body - The `<tbody>` element.
|
|
19
|
+
* @part row - Each `<tr>` in the body.
|
|
20
|
+
* @part header-cell - Each `<th>` in the header row.
|
|
21
|
+
* @part cell - Each `<td>` in a body row.
|
|
22
|
+
*/
|
|
23
|
+
export class BsDataTable {
|
|
24
|
+
/** Array prop -- must be set as a JS property (`el.columns = [...]`), not an HTML attribute,
|
|
25
|
+
* since attributes can only carry strings. See CONVENTIONS.md. */
|
|
26
|
+
columns = [];
|
|
27
|
+
/** Array prop -- same JS-property-only rule as `columns`. */
|
|
28
|
+
rows = [];
|
|
29
|
+
sortColumn;
|
|
30
|
+
sortDirection = 'asc';
|
|
31
|
+
selectable = false;
|
|
32
|
+
/** Optional custom cell renderer, e.g. for an actions column. Function props are JS-property-only,
|
|
33
|
+
* same as array props -- there is no attribute equivalent. Falls back to the raw cell value. */
|
|
34
|
+
cellRenderer;
|
|
35
|
+
bsSort;
|
|
36
|
+
bsRowSelect;
|
|
37
|
+
selectedIds = new Set();
|
|
38
|
+
onHeaderClick = (column) => {
|
|
39
|
+
if (!column.sortable)
|
|
40
|
+
return;
|
|
41
|
+
const direction = this.sortColumn === column.key && this.sortDirection === 'asc' ? 'desc' : 'asc';
|
|
42
|
+
this.bsSort.emit({ column: column.key, direction });
|
|
43
|
+
};
|
|
44
|
+
onRowCheckboxChange = (row, ev) => {
|
|
45
|
+
const selected = ev.target.checked;
|
|
46
|
+
const next = new Set(this.selectedIds);
|
|
47
|
+
if (selected) {
|
|
48
|
+
next.add(row.id);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
next.delete(row.id);
|
|
52
|
+
}
|
|
53
|
+
this.selectedIds = next;
|
|
54
|
+
this.bsRowSelect.emit({ id: row.id, selected });
|
|
55
|
+
};
|
|
56
|
+
render() {
|
|
57
|
+
return (h("table", { key: '9d51da1ed65a523ac8008fde2f77673a68e4e39e', part: "table", class: "bs-data-table" }, h("thead", { key: 'e1baef3845a07af164e2c91cd3979d1ff76eb767', part: "head" }, h("tr", { key: '930648d2ced662352ddfb7dfc9748501324e7bf3' }, this.selectable && h("th", { key: 'd61beed09258e8ba9c087d4d922eebb6591735e9', class: "bs-data-table__select-col" }), this.columns.map(column => {
|
|
58
|
+
const isSorted = this.sortColumn === column.key;
|
|
59
|
+
const ariaSort = column.sortable ? (isSorted ? (this.sortDirection === 'asc' ? 'ascending' : 'descending') : 'none') : undefined;
|
|
60
|
+
return (h("th", { part: "header-cell", class: "bs-data-table__header-cell", "aria-sort": ariaSort }, column.sortable ? (h("button", { type: "button", class: "bs-data-table__sort-button", onClick: () => this.onHeaderClick(column) }, column.label, isSorted && h("span", { class: "bs-data-table__sort-indicator" }, this.sortDirection === 'asc' ? ' ▲' : ' ▼'))) : (column.label)));
|
|
61
|
+
}))), h("tbody", { key: '2015f29ec50c9e99ce63366ea06a4d9082fc638d', part: "body" }, this.rows.map(row => (h("tr", { part: "row", class: "bs-data-table__row" }, this.selectable && (h("td", { class: "bs-data-table__select-col" }, h("input", { type: "checkbox", checked: this.selectedIds.has(row.id), onChange: ev => this.onRowCheckboxChange(row, ev) }))), this.columns.map(column => this.cellRenderer ? (
|
|
62
|
+
// cellRenderer is a developer-supplied, trusted function (JS-property-only, never
|
|
63
|
+
// derived from untrusted input) -- its HTML output is intentionally not escaped,
|
|
64
|
+
// same trust model as React's dangerouslySetInnerHTML.
|
|
65
|
+
h("td", { part: "cell", class: "bs-data-table__cell", innerHTML: this.cellRenderer(row, column) })) : (
|
|
66
|
+
// Default path renders raw cell values as plain text so untrusted row data
|
|
67
|
+
// (e.g. user-submitted names) can never be interpreted as HTML.
|
|
68
|
+
h("td", { part: "cell", class: "bs-data-table__cell" }, String(row[column.key] ?? ''))))))))));
|
|
69
|
+
}
|
|
70
|
+
static get is() { return "bs-data-table"; }
|
|
71
|
+
static get encapsulation() { return "shadow"; }
|
|
72
|
+
static get originalStyleUrls() {
|
|
73
|
+
return {
|
|
74
|
+
"$": ["bs-data-table.css"]
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
static get styleUrls() {
|
|
78
|
+
return {
|
|
79
|
+
"$": ["bs-data-table.css"]
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
static get properties() {
|
|
83
|
+
return {
|
|
84
|
+
"columns": {
|
|
85
|
+
"type": "unknown",
|
|
86
|
+
"mutable": false,
|
|
87
|
+
"complexType": {
|
|
88
|
+
"original": "BsDataTableColumn[]",
|
|
89
|
+
"resolved": "BsDataTableColumn[]",
|
|
90
|
+
"references": {
|
|
91
|
+
"BsDataTableColumn": {
|
|
92
|
+
"location": "local",
|
|
93
|
+
"path": "/Users/vivka/brandsync-web-components/src/components/bs-data-table/bs-data-table.tsx",
|
|
94
|
+
"id": "src/components/bs-data-table/bs-data-table.tsx::BsDataTableColumn"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"required": false,
|
|
99
|
+
"optional": false,
|
|
100
|
+
"docs": {
|
|
101
|
+
"tags": [],
|
|
102
|
+
"text": "Array prop -- must be set as a JS property (`el.columns = [...]`), not an HTML attribute,\nsince attributes can only carry strings. See CONVENTIONS.md."
|
|
103
|
+
},
|
|
104
|
+
"getter": false,
|
|
105
|
+
"setter": false,
|
|
106
|
+
"defaultValue": "[]"
|
|
107
|
+
},
|
|
108
|
+
"rows": {
|
|
109
|
+
"type": "unknown",
|
|
110
|
+
"mutable": false,
|
|
111
|
+
"complexType": {
|
|
112
|
+
"original": "BsDataTableRow[]",
|
|
113
|
+
"resolved": "BsDataTableRow[]",
|
|
114
|
+
"references": {
|
|
115
|
+
"BsDataTableRow": {
|
|
116
|
+
"location": "local",
|
|
117
|
+
"path": "/Users/vivka/brandsync-web-components/src/components/bs-data-table/bs-data-table.tsx",
|
|
118
|
+
"id": "src/components/bs-data-table/bs-data-table.tsx::BsDataTableRow"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
"required": false,
|
|
123
|
+
"optional": false,
|
|
124
|
+
"docs": {
|
|
125
|
+
"tags": [],
|
|
126
|
+
"text": "Array prop -- same JS-property-only rule as `columns`."
|
|
127
|
+
},
|
|
128
|
+
"getter": false,
|
|
129
|
+
"setter": false,
|
|
130
|
+
"defaultValue": "[]"
|
|
131
|
+
},
|
|
132
|
+
"sortColumn": {
|
|
133
|
+
"type": "string",
|
|
134
|
+
"mutable": false,
|
|
135
|
+
"complexType": {
|
|
136
|
+
"original": "string",
|
|
137
|
+
"resolved": "string",
|
|
138
|
+
"references": {}
|
|
139
|
+
},
|
|
140
|
+
"required": false,
|
|
141
|
+
"optional": true,
|
|
142
|
+
"docs": {
|
|
143
|
+
"tags": [],
|
|
144
|
+
"text": ""
|
|
145
|
+
},
|
|
146
|
+
"getter": false,
|
|
147
|
+
"setter": false,
|
|
148
|
+
"reflect": false,
|
|
149
|
+
"attribute": "sort-column"
|
|
150
|
+
},
|
|
151
|
+
"sortDirection": {
|
|
152
|
+
"type": "string",
|
|
153
|
+
"mutable": false,
|
|
154
|
+
"complexType": {
|
|
155
|
+
"original": "'asc' | 'desc'",
|
|
156
|
+
"resolved": "\"asc\" | \"desc\"",
|
|
157
|
+
"references": {}
|
|
158
|
+
},
|
|
159
|
+
"required": false,
|
|
160
|
+
"optional": false,
|
|
161
|
+
"docs": {
|
|
162
|
+
"tags": [],
|
|
163
|
+
"text": ""
|
|
164
|
+
},
|
|
165
|
+
"getter": false,
|
|
166
|
+
"setter": false,
|
|
167
|
+
"reflect": false,
|
|
168
|
+
"attribute": "sort-direction",
|
|
169
|
+
"defaultValue": "'asc'"
|
|
170
|
+
},
|
|
171
|
+
"selectable": {
|
|
172
|
+
"type": "boolean",
|
|
173
|
+
"mutable": false,
|
|
174
|
+
"complexType": {
|
|
175
|
+
"original": "boolean",
|
|
176
|
+
"resolved": "boolean",
|
|
177
|
+
"references": {}
|
|
178
|
+
},
|
|
179
|
+
"required": false,
|
|
180
|
+
"optional": false,
|
|
181
|
+
"docs": {
|
|
182
|
+
"tags": [],
|
|
183
|
+
"text": ""
|
|
184
|
+
},
|
|
185
|
+
"getter": false,
|
|
186
|
+
"setter": false,
|
|
187
|
+
"reflect": false,
|
|
188
|
+
"attribute": "selectable",
|
|
189
|
+
"defaultValue": "false"
|
|
190
|
+
},
|
|
191
|
+
"cellRenderer": {
|
|
192
|
+
"type": "unknown",
|
|
193
|
+
"mutable": false,
|
|
194
|
+
"complexType": {
|
|
195
|
+
"original": "(row: BsDataTableRow, column: BsDataTableColumn) => string",
|
|
196
|
+
"resolved": "(row: BsDataTableRow, column: BsDataTableColumn) => string",
|
|
197
|
+
"references": {
|
|
198
|
+
"BsDataTableRow": {
|
|
199
|
+
"location": "local",
|
|
200
|
+
"path": "/Users/vivka/brandsync-web-components/src/components/bs-data-table/bs-data-table.tsx",
|
|
201
|
+
"id": "src/components/bs-data-table/bs-data-table.tsx::BsDataTableRow"
|
|
202
|
+
},
|
|
203
|
+
"BsDataTableColumn": {
|
|
204
|
+
"location": "local",
|
|
205
|
+
"path": "/Users/vivka/brandsync-web-components/src/components/bs-data-table/bs-data-table.tsx",
|
|
206
|
+
"id": "src/components/bs-data-table/bs-data-table.tsx::BsDataTableColumn"
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
"required": false,
|
|
211
|
+
"optional": true,
|
|
212
|
+
"docs": {
|
|
213
|
+
"tags": [],
|
|
214
|
+
"text": "Optional custom cell renderer, e.g. for an actions column. Function props are JS-property-only,\nsame as array props -- there is no attribute equivalent. Falls back to the raw cell value."
|
|
215
|
+
},
|
|
216
|
+
"getter": false,
|
|
217
|
+
"setter": false
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
static get states() {
|
|
222
|
+
return {
|
|
223
|
+
"selectedIds": {}
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
static get events() {
|
|
227
|
+
return [{
|
|
228
|
+
"method": "bsSort",
|
|
229
|
+
"name": "bsSort",
|
|
230
|
+
"bubbles": true,
|
|
231
|
+
"cancelable": true,
|
|
232
|
+
"composed": true,
|
|
233
|
+
"docs": {
|
|
234
|
+
"tags": [],
|
|
235
|
+
"text": ""
|
|
236
|
+
},
|
|
237
|
+
"complexType": {
|
|
238
|
+
"original": "{ column: string; direction: 'asc' | 'desc' }",
|
|
239
|
+
"resolved": "{ column: string; direction: \"desc\" | \"asc\"; }",
|
|
240
|
+
"references": {}
|
|
241
|
+
}
|
|
242
|
+
}, {
|
|
243
|
+
"method": "bsRowSelect",
|
|
244
|
+
"name": "bsRowSelect",
|
|
245
|
+
"bubbles": true,
|
|
246
|
+
"cancelable": true,
|
|
247
|
+
"composed": true,
|
|
248
|
+
"docs": {
|
|
249
|
+
"tags": [],
|
|
250
|
+
"text": ""
|
|
251
|
+
},
|
|
252
|
+
"complexType": {
|
|
253
|
+
"original": "{ id: string | number; selected: boolean }",
|
|
254
|
+
"resolved": "{ id: string | number; selected: boolean; }",
|
|
255
|
+
"references": {}
|
|
256
|
+
}
|
|
257
|
+
}];
|
|
258
|
+
}
|
|
259
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { componentDescription } from "../../stories-utils";
|
|
2
|
+
const meta = {
|
|
3
|
+
title: 'Components/bs-data-table',
|
|
4
|
+
parameters: { docs: { description: { component: componentDescription('bs-data-table') } } },
|
|
5
|
+
};
|
|
6
|
+
export default meta;
|
|
7
|
+
const columns = [
|
|
8
|
+
{ key: 'name', label: 'Name', sortable: true },
|
|
9
|
+
{ key: 'department', label: 'Department', sortable: true },
|
|
10
|
+
{ key: 'status', label: 'Status' },
|
|
11
|
+
];
|
|
12
|
+
const rows = [
|
|
13
|
+
{ id: 1, name: 'Amelia Torres', department: 'Facilities', status: 'Active' },
|
|
14
|
+
{ id: 2, name: 'Noah Whitfield', department: 'Engineering', status: 'Active' },
|
|
15
|
+
{ id: 3, name: 'Priya Nair', department: 'Design', status: 'On leave' },
|
|
16
|
+
];
|
|
17
|
+
export const Default = {
|
|
18
|
+
render: () => {
|
|
19
|
+
const table = document.createElement('bs-data-table');
|
|
20
|
+
table.columns = columns;
|
|
21
|
+
table.rows = rows;
|
|
22
|
+
table.addEventListener('bsSort', (ev) => {
|
|
23
|
+
// eslint-disable-next-line no-console
|
|
24
|
+
console.log('bsSort', ev.detail);
|
|
25
|
+
});
|
|
26
|
+
return table;
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
export const Selectable = {
|
|
30
|
+
render: () => {
|
|
31
|
+
const table = document.createElement('bs-data-table');
|
|
32
|
+
table.columns = columns;
|
|
33
|
+
table.rows = rows;
|
|
34
|
+
table.selectable = true;
|
|
35
|
+
table.addEventListener('bsRowSelect', (ev) => {
|
|
36
|
+
// eslint-disable-next-line no-console
|
|
37
|
+
console.log('bsRowSelect', ev.detail);
|
|
38
|
+
});
|
|
39
|
+
return table;
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
export const WithCustomCellRenderer = {
|
|
43
|
+
render: () => {
|
|
44
|
+
const table = document.createElement('bs-data-table');
|
|
45
|
+
table.columns = [...columns, { key: 'actions', label: 'Actions' }];
|
|
46
|
+
table.rows = rows;
|
|
47
|
+
table.cellRenderer = (row, column) => {
|
|
48
|
+
if (column.key === 'actions') {
|
|
49
|
+
return `<button type="button">Edit</button>`;
|
|
50
|
+
}
|
|
51
|
+
return String(row[column.key] ?? '');
|
|
52
|
+
};
|
|
53
|
+
return table;
|
|
54
|
+
},
|
|
55
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { render, h, describe, it, expect } from "@stencil/vitest";
|
|
2
|
+
describe('bs-input', () => {
|
|
3
|
+
it('renders a native text input with an empty value by default', async () => {
|
|
4
|
+
const { root } = await render(h("bs-input", null));
|
|
5
|
+
const input = root.shadowRoot.querySelector('input');
|
|
6
|
+
expect(input).toEqualAttribute('type', 'text');
|
|
7
|
+
expect(input.value).toBe('');
|
|
8
|
+
expect(input).not.toHaveAttribute('disabled');
|
|
9
|
+
});
|
|
10
|
+
it('reflects the value prop to the native input', async () => {
|
|
11
|
+
const { root } = await render(h("bs-input", { value: "hello" }));
|
|
12
|
+
const input = root.shadowRoot.querySelector('input');
|
|
13
|
+
expect(input.value).toBe('hello');
|
|
14
|
+
});
|
|
15
|
+
['text', 'email', 'password', 'number'].forEach(type => {
|
|
16
|
+
it(`applies type="${type}" to the native input`, async () => {
|
|
17
|
+
const { root } = await render(h("bs-input", { type: type }));
|
|
18
|
+
const input = root.shadowRoot.querySelector('input');
|
|
19
|
+
expect(input).toEqualAttribute('type', type);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
it('passes the placeholder prop through to the native input', async () => {
|
|
23
|
+
const { root } = await render(h("bs-input", { placeholder: "Enter your name" }));
|
|
24
|
+
const input = root.shadowRoot.querySelector('input');
|
|
25
|
+
expect(input).toEqualAttribute('placeholder', 'Enter your name');
|
|
26
|
+
});
|
|
27
|
+
it('does not render a label when the label prop is not set', async () => {
|
|
28
|
+
const { root } = await render(h("bs-input", null));
|
|
29
|
+
expect(root.shadowRoot.querySelector('label')).toBeNull();
|
|
30
|
+
});
|
|
31
|
+
it('renders a label wired to the control via htmlFor/id when the label prop is set', async () => {
|
|
32
|
+
const { root } = await render(h("bs-input", { label: "Email address" }));
|
|
33
|
+
const label = root.shadowRoot.querySelector('label');
|
|
34
|
+
const input = root.shadowRoot.querySelector('input');
|
|
35
|
+
expect(label).toHaveTextContent('Email address');
|
|
36
|
+
expect(label).toEqualAttribute('for', 'control');
|
|
37
|
+
expect(input).toEqualAttribute('id', 'control');
|
|
38
|
+
expect(label).toEqualAttribute('part', 'label');
|
|
39
|
+
});
|
|
40
|
+
it('disables the native input when disabled is set', async () => {
|
|
41
|
+
const { root } = await render(h("bs-input", { disabled: true }));
|
|
42
|
+
const input = root.shadowRoot.querySelector('input');
|
|
43
|
+
expect(input).toHaveAttribute('disabled');
|
|
44
|
+
});
|
|
45
|
+
it('renders the description when set and no error is present', async () => {
|
|
46
|
+
const { root } = await render(h("bs-input", { description: "Must be a valid email" }));
|
|
47
|
+
const description = root.shadowRoot.querySelector('[part="description"]');
|
|
48
|
+
const input = root.shadowRoot.querySelector('input');
|
|
49
|
+
expect(description).toHaveTextContent('Must be a valid email');
|
|
50
|
+
expect(root.shadowRoot.querySelector('[part="error"]')).toBeNull();
|
|
51
|
+
expect(input).toEqualAttribute('aria-describedby', 'description-or-error');
|
|
52
|
+
expect(input).not.toHaveAttribute('aria-invalid');
|
|
53
|
+
});
|
|
54
|
+
it('renders the error message instead of the description, applies the error class, and sets aria-invalid', async () => {
|
|
55
|
+
const { root } = await render(h("bs-input", { description: "Must be a valid email", error: "This field is required" }));
|
|
56
|
+
const error = root.shadowRoot.querySelector('[part="error"]');
|
|
57
|
+
expect(error).toHaveTextContent('This field is required');
|
|
58
|
+
expect(root.shadowRoot.querySelector('[part="description"]')).toBeNull();
|
|
59
|
+
const wrapper = root.shadowRoot.querySelector('.bs-input');
|
|
60
|
+
expect(wrapper).toHaveClass('bs-input--error');
|
|
61
|
+
const input = root.shadowRoot.querySelector('input');
|
|
62
|
+
expect(input).toEqualAttribute('aria-invalid', 'true');
|
|
63
|
+
expect(input).toEqualAttribute('aria-describedby', 'description-or-error');
|
|
64
|
+
});
|
|
65
|
+
it('emits bsInput with the current value as the user types', async () => {
|
|
66
|
+
const { root, spyOnEvent } = await render(h("bs-input", null));
|
|
67
|
+
const bsInputSpy = spyOnEvent('bsInput');
|
|
68
|
+
const input = root.shadowRoot.querySelector('input');
|
|
69
|
+
input.value = 'a';
|
|
70
|
+
input.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
|
71
|
+
expect(bsInputSpy).toHaveReceivedEventTimes(1);
|
|
72
|
+
expect(bsInputSpy).toHaveReceivedEventDetail('a');
|
|
73
|
+
});
|
|
74
|
+
it('emits bsChange with the current value on native change', async () => {
|
|
75
|
+
const { root, spyOnEvent } = await render(h("bs-input", null));
|
|
76
|
+
const bsChangeSpy = spyOnEvent('bsChange');
|
|
77
|
+
const input = root.shadowRoot.querySelector('input');
|
|
78
|
+
input.value = 'final value';
|
|
79
|
+
input.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
|
|
80
|
+
expect(bsChangeSpy).toHaveReceivedEventTimes(1);
|
|
81
|
+
expect(bsChangeSpy).toHaveReceivedEventDetail('final value');
|
|
82
|
+
});
|
|
83
|
+
it('does not emit bsChange on input, or bsInput on change', async () => {
|
|
84
|
+
const { root, spyOnEvent } = await render(h("bs-input", null));
|
|
85
|
+
const bsInputSpy = spyOnEvent('bsInput');
|
|
86
|
+
const bsChangeSpy = spyOnEvent('bsChange');
|
|
87
|
+
const input = root.shadowRoot.querySelector('input');
|
|
88
|
+
input.value = 'x';
|
|
89
|
+
input.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
|
90
|
+
expect(bsChangeSpy.length).toBe(0);
|
|
91
|
+
input.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
|
|
92
|
+
expect(bsInputSpy).toHaveReceivedEventTimes(1);
|
|
93
|
+
});
|
|
94
|
+
it('delegates focus from the host to the native input (delegatesFocus)', async () => {
|
|
95
|
+
const { root } = await render(h("bs-input", { label: "Name" }));
|
|
96
|
+
const input = root.shadowRoot.querySelector('input');
|
|
97
|
+
root.focus();
|
|
98
|
+
expect(root.shadowRoot.activeElement).toBe(input);
|
|
99
|
+
expect(document.activeElement).toBe(root);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
display: block;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.bs-input {
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
gap: var(--bs-spacing-100);
|
|
9
|
+
font-family: inherit;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.bs-input__label {
|
|
13
|
+
font-size: var(--bs-font-size-sm);
|
|
14
|
+
font-weight: var(--bs-font-weight-medium);
|
|
15
|
+
color: var(--bs-input-label);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.bs-input__control {
|
|
19
|
+
height: var(--bs-spacing-600);
|
|
20
|
+
padding: 0 var(--bs-spacing-300);
|
|
21
|
+
border: 1px solid var(--bs-input-border-default);
|
|
22
|
+
border-radius: var(--bs-border-radius-100);
|
|
23
|
+
background: var(--bs-input-bg-default);
|
|
24
|
+
color: var(--bs-input-text);
|
|
25
|
+
font-size: var(--bs-font-size-md);
|
|
26
|
+
font-family: inherit;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.bs-input__control::placeholder {
|
|
30
|
+
color: var(--bs-input-placeholder);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.bs-input__control:hover:not(:disabled) {
|
|
34
|
+
border-color: var(--bs-input-border-hover);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.bs-input__control:focus-visible {
|
|
38
|
+
outline: none;
|
|
39
|
+
border-color: var(--bs-input-border-focus);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.bs-input__control:disabled {
|
|
43
|
+
background: var(--bs-input-bg-disabled);
|
|
44
|
+
color: var(--bs-input-text-disabled);
|
|
45
|
+
cursor: not-allowed;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.bs-input--error .bs-input__control {
|
|
49
|
+
border-color: var(--bs-input-border-error);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.bs-input--error .bs-input__label {
|
|
53
|
+
color: var(--bs-input-label);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.bs-input__description {
|
|
57
|
+
font-size: var(--bs-font-size-xs);
|
|
58
|
+
color: var(--bs-input-description);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.bs-input__error {
|
|
62
|
+
font-size: var(--bs-font-size-xs);
|
|
63
|
+
color: var(--bs-input-error);
|
|
64
|
+
}
|