@spectric/ui 0.0.19 → 0.0.21

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.
@@ -1,91 +1,179 @@
1
- import { test, expect } from '@playwright/test';
2
- import type { SpectricTableElement, TableEvents } from '../index';
3
- import type SpectricModule from "../../../index.ts"
1
+ import { test, expect } from "@playwright/test";
2
+ import type { SpectricTableElement, TableEvents } from "../index";
3
+ import type SpectricModule from "../../../index.ts";
4
+ import { SpectricInput } from "../../../index.ts";
4
5
  test.beforeEach(async ({ page }) => {
5
- page.on('console', msg => { console.log(`Page[${msg.type()}]:` + msg.text()) });
6
+ page.on("console", (msg) => {
7
+ console.log(`Page[${msg.type()}]:` + msg.text());
8
+ });
6
9
  await page.addInitScript(() => {
7
10
  //@ts-ignore
8
11
  window.awaitEvent = function awaitEvent(target, event, trigger) {
9
12
  return new Promise((resolve, reject) => {
13
+ let timeout = setTimeout(reject, 400);
10
14
  const handleEvent = (...args) => {
15
+ clearTimeout(timeout);
11
16
  //@ts-ignore
12
- target.removeEventListener(event, this)
13
- resolve(args)
14
- }
17
+ target.removeEventListener(event, this);
18
+ resolve(args);
19
+ };
15
20
  //@ts-ignore
16
- target.addEventListener(event, handleEvent)
17
- trigger()
18
- })
19
- }
20
- })
21
- await page.goto('http://localhost:6006/iframe.html?globals=&args=&id=spectric-ui-components-ui-table--milti-select&viewMode=story');
21
+ target.addEventListener(event, handleEvent);
22
+ trigger();
23
+ });
24
+ };
25
+ });
22
26
  });
23
- test.describe("Spectric Table Tests", () => {
24
27
 
25
- test('MultiSelect Table Select All/Deselect All ', async ({ page }) => {
26
- let tableLocator = page.locator("spectric-table")
27
- tableLocator.waitFor()
28
+ test.describe("Spectric Table Tests", () => {
29
+ test("MultiSelect Table Select All/Deselect All ", async ({ page }) => {
30
+ await page.goto(
31
+ "http://localhost:6006/iframe.html?globals=&args=&id=spectric-ui-components-ui-table--milti-select&viewMode=story"
32
+ );
33
+ let tableLocator = page.locator("spectric-table");
34
+ tableLocator.waitFor();
28
35
  var selected = await tableLocator.evaluate<number, SpectricTableElement>(
29
36
  async (table) => {
30
- let selectAll = table.querySelectorAll<HTMLElement>("spectric-table-header spectric-input[variant='checkbox'] spectric-button")[0]
31
- let [event] = await window.awaitEvent<TableEvents>(table, "selected", () => selectAll.click()) as Parameters<TableEvents["selected"]>
32
- return event.detail.length
37
+ let selectAll = table.querySelectorAll<HTMLElement>(
38
+ "spectric-table-header spectric-input[variant='checkbox'] spectric-button"
39
+ )[0];
40
+ let [event] = (await window.awaitEvent<TableEvents>(
41
+ table,
42
+ "selected",
43
+ () => selectAll.click()
44
+ )) as Parameters<TableEvents["selected"]>;
45
+ return event.detail.length;
33
46
  }
34
- )
35
- await expect(selected, "All Selected").toBeGreaterThan(0)
47
+ );
48
+ await expect(selected, "All Selected").toBeGreaterThan(0);
36
49
 
37
50
  selected = await tableLocator.evaluate<number, SpectricTableElement>(
38
51
  async (table) => {
39
- let selectAll = table.querySelectorAll<HTMLElement>("spectric-table-header spectric-input[variant='checkbox'] spectric-button")[0]
40
- let [event] = await window.awaitEvent<TableEvents>(table, "selected", () => selectAll.click()) as Parameters<TableEvents["selected"]>
41
- return event.detail.length
52
+ let selectAll = table.querySelectorAll<HTMLElement>(
53
+ "spectric-table-header spectric-input[variant='checkbox'] spectric-button"
54
+ )[0];
55
+ let [event] = (await window.awaitEvent<TableEvents>(
56
+ table,
57
+ "selected",
58
+ () => selectAll.click()
59
+ )) as Parameters<TableEvents["selected"]>;
60
+ return event.detail.length;
42
61
  }
43
- )
44
- await expect(selected, "None Selected").toBe(0)
62
+ );
63
+ await expect(selected, "None Selected").toBe(0);
45
64
 
46
65
  selected = await tableLocator.evaluate<number, SpectricTableElement>(
47
66
  async (table) => {
48
- let selectAll = table.querySelectorAll<HTMLElement>("spectric-table-virtual-body spectric-input[variant='checkbox'] spectric-button")[2]
49
- let [event] = await window.awaitEvent<TableEvents>(table, "selected", () => selectAll.click()) as Parameters<TableEvents["selected"]>
50
- return event.detail.length
67
+ let selectAll = table.querySelectorAll<HTMLElement>(
68
+ "spectric-table-virtual-body spectric-input[variant='checkbox'] spectric-button"
69
+ )[2];
70
+ let [event] = (await window.awaitEvent<TableEvents>(
71
+ table,
72
+ "selected",
73
+ () => selectAll.click()
74
+ )) as Parameters<TableEvents["selected"]>;
75
+ return event.detail.length;
51
76
  }
52
- )
53
- await expect(selected, "One Selected").toBe(1)
77
+ );
78
+ await expect(selected, "One Selected").toBe(1);
54
79
 
55
80
  selected = await tableLocator.evaluate<number, SpectricTableElement>(
56
81
  async (table) => {
57
- let selectAll = table.querySelectorAll<HTMLElement>("spectric-table-virtual-body spectric-input[variant='checkbox'] spectric-button")[2]
58
- let [event] = await window.awaitEvent<TableEvents>(table, "selected", () => selectAll.click()) as Parameters<TableEvents["selected"]>
59
- return event.detail.length
82
+ let selectAll = table.querySelectorAll<HTMLElement>(
83
+ "spectric-table-virtual-body spectric-input[variant='checkbox'] spectric-button"
84
+ )[2];
85
+ let [event] = (await window.awaitEvent<TableEvents>(
86
+ table,
87
+ "selected",
88
+ () => selectAll.click()
89
+ )) as Parameters<TableEvents["selected"]>;
90
+ return event.detail.length;
60
91
  }
61
- )
62
- await expect(selected, "One DeSelected").toBe(0)
92
+ );
93
+ await expect(selected, "One DeSelected").toBe(0);
63
94
  });
64
95
 
96
+ test("SingleSelect Table ensure only one is selected", async ({ page }) => {
97
+ await page.goto(
98
+ "http://localhost:6006/iframe.html?globals=&args=&id=spectric-ui-components-ui-table--single-select&viewMode=story"
99
+ );
100
+ let tableLocator = page.locator("spectric-table");
101
+ tableLocator.waitFor();
102
+ var selected = await tableLocator.evaluate<number, SpectricTableElement>(
103
+ async (table) => {
104
+ let select = table.querySelectorAll<HTMLElement>(
105
+ "spectric-input[variant='checkbox'].table-checkbox-single spectric-button"
106
+ )[0];
107
+ let [event] = (await window.awaitEvent<TableEvents>(
108
+ table,
109
+ "selected",
110
+ () => select.click()
111
+ )) as Parameters<TableEvents["selected"]>;
112
+ return event.detail.length;
113
+ }
114
+ );
115
+ await expect(selected, "One Selected").toBe(1);
116
+
117
+ selected = await tableLocator.evaluate<number, SpectricTableElement>(
118
+ async (table) => {
119
+ let select = table.querySelectorAll<HTMLElement>(
120
+ "spectric-input[variant='checkbox'].table-checkbox-single spectric-button"
121
+ )[1];
122
+ let [event] = (await window.awaitEvent<TableEvents>(
123
+ table,
124
+ "selected",
125
+ () => select.click()
126
+ )) as Parameters<TableEvents["selected"]>;
127
+ return event.detail.length;
128
+ }
129
+ );
130
+ await expect(selected, "One Selected").toBe(1);
131
+
132
+ let checked = await tableLocator.evaluate<number, SpectricTableElement>(
133
+ async (table) => {
134
+ let selectAll = table.querySelectorAll<HTMLElement>(
135
+ "spectric-input[variant='checkbox'].table-checkbox-single"
136
+ )[2];
137
+ let checked = Array.from(
138
+ table.querySelectorAll<SpectricInput>(
139
+ "spectric-input[variant='checkbox'].table-checkbox-single"
140
+ )
141
+ ).filter((node) => node.checked);
142
+ return checked.length;
143
+ }
144
+ );
145
+ await expect(checked, "One checked").toBe(1);
146
+ });
65
147
 
66
- test('Ensure all event listeners are cleaned up and not leaked', async ({ page }) => {
67
- let tableLocator = page.locator("spectric-table")
68
- await tableLocator.waitFor()
148
+ test("Ensure all event listeners are cleaned up and not leaked", async ({
149
+ page,
150
+ }) => {
151
+ await page.goto(
152
+ "http://localhost:6006/iframe.html?globals=&args=&id=spectric-ui-components-ui-table--milti-select&viewMode=story"
153
+ );
154
+ let tableLocator = page.locator("spectric-table");
155
+ await tableLocator.waitFor();
69
156
  let listenerCount = await page.evaluate(async () => {
70
- const module = await import("./src/index.ts") as typeof SpectricModule
71
- return module.getListeners()
72
- })
73
- await expect(listenerCount).toBeGreaterThan(0)
157
+ const module = (await import("./src/index.ts")) as typeof SpectricModule;
158
+ return module.getListeners();
159
+ });
160
+ await expect(listenerCount).toBeGreaterThan(0);
74
161
  //Delete all content and expect everything to get cleaned up
75
162
  listenerCount = await page.evaluate(async () => {
76
- const module = await import("./src/index.ts") as typeof SpectricModule
77
- document.body.innerHTML = ""
78
- return module.getListeners()
79
- })
80
- await expect(listenerCount).toBe(0)
81
-
82
- })
83
- })
84
-
85
-
163
+ const module = (await import("./src/index.ts")) as typeof SpectricModule;
164
+ document.body.innerHTML = "";
165
+ return module.getListeners();
166
+ });
167
+ await expect(listenerCount).toBe(0);
168
+ });
169
+ });
86
170
 
87
171
  declare global {
88
172
  interface Window {
89
- awaitEvent: <EventMap extends Record<string, any>, K = keyof EventMap>(target: HTMLElement, event: K, trigger: () => void) => Parameters<EventMap[keyof EventMap]>;
173
+ awaitEvent: <EventMap extends Record<string, any>, K = keyof EventMap>(
174
+ target: HTMLElement,
175
+ event: K,
176
+ trigger: () => void
177
+ ) => Parameters<EventMap[keyof EventMap]>;
90
178
  }
91
179
  }
@@ -1,177 +1,216 @@
1
- import { html, LitElement, PropertyValues } from 'lit';
2
- import { customElement, property, query, state, } from 'lit/decorators.js';
3
- import { HTMLElementTagWithEvents, ReactElementWithPropsAndEvents } from '../types';
4
- export const TableCellElementTag = "spectric-table-cell"
5
- import { ColumnSettings, DomRenderable, SpectricTableElement } from './table';
6
- import { cache } from 'lit/directives/cache.js';
7
- import { StyleInfo, styleMap } from 'lit/directives/style-map.js';
1
+ import { html, LitElement, PropertyValues } from "lit";
2
+ import { customElement, property, query, state } from "lit/decorators.js";
3
+ import {
4
+ HTMLElementTagWithEvents,
5
+ ReactElementWithPropsAndEvents,
6
+ } from "../types";
7
+ export const TableCellElementTag = "spectric-table-cell";
8
+ import { ColumnSettings, DomRenderable, SpectricTableElement } from "./table";
9
+ import { cache } from "lit/directives/cache.js";
10
+ import { StyleInfo, styleMap } from "lit/directives/style-map.js";
8
11
 
9
12
  interface CellProps<T> {
10
- column: ColumnSettings<T>
11
- row: T
13
+ column: ColumnSettings<T>;
14
+ row: T;
12
15
  }
13
16
 
14
17
  export type FilterEvent<T> = {
15
- value?: T,
16
- include: boolean
17
- column: ColumnSettings<T>
18
- row: T
19
- }
18
+ value?: T;
19
+ include: boolean;
20
+ column: ColumnSettings<T>;
21
+ row: T;
22
+ };
20
23
 
21
24
  /**
22
25
  * Pagination Element
23
26
  */
24
27
  @customElement(TableCellElementTag)
25
28
  export class TableCellElement<T> extends LitElement implements CellProps<T> {
26
- @property({ type: Object, attribute: false })
27
- row!: T;
28
-
29
- @property({ type: Number, attribute: false })
30
- index!: number;
31
- @property({ type: Object, attribute: false })
32
- column!: ColumnSettings<T>;
33
- columns!: ColumnSettings<T>[];
34
- table!: SpectricTableElement<T>
35
-
36
- @state()
37
- overflow: DomRenderable = ""
38
- @query("td")
39
- td!: HTMLTableCellElement
40
- styleRules: StyleInfo = { whiteSpace: "", width: "" };
41
- protected createRenderRoot(): HTMLElement | DocumentFragment {
42
- return this
43
- }
44
- protected updated(_changedProperties: PropertyValues): void {
29
+ @property({ type: Object, attribute: false })
30
+ row!: T;
45
31
 
46
- }
47
- _emitFilter(filter: FilterEvent<T>) {
48
- this.dispatchEvent(new CustomEvent<FilterEvent<T>>("filter", {
49
- composed: true,
50
- bubbles: true,
51
- detail: filter
52
- }))
53
- }
54
- _handleFilterOut = () => {
55
- let value = undefined;
56
- if (this.column.key && typeof this.row === "object") {
57
- value = rowGetValue(this.row as Record<any, any>, this.column.key)
58
- }
59
- this._emitFilter({
60
- include: false,
61
- row: this.row,
62
- value,
63
- column: this.column
64
- })
32
+ @property({ type: Number, attribute: false })
33
+ index!: number;
34
+ @property({ type: Object, attribute: false })
35
+ column!: ColumnSettings<T>;
36
+ columns!: ColumnSettings<T>[];
37
+ @property({ type: Object, attribute: false })
38
+ table!: SpectricTableElement<T>;
65
39
 
40
+ @state()
41
+ overflow: DomRenderable = "";
42
+ @query("td")
43
+ td!: HTMLTableCellElement;
44
+ styleRules: StyleInfo = { whiteSpace: "", width: "" };
45
+ protected createRenderRoot(): HTMLElement | DocumentFragment {
46
+ return this;
47
+ }
48
+ protected updated(_changedProperties: PropertyValues): void {}
49
+ _emitFilter(filter: FilterEvent<T>) {
50
+ this.dispatchEvent(
51
+ new CustomEvent<FilterEvent<T>>("filter", {
52
+ composed: true,
53
+ bubbles: true,
54
+ detail: filter,
55
+ })
56
+ );
57
+ }
58
+ _handleFilterOut = () => {
59
+ let value = undefined;
60
+ if (this.column.key && typeof this.row === "object") {
61
+ value = rowGetValue(this.row as Record<any, any>, this.column.key);
66
62
  }
67
- _handleFilterFor = () => {
68
- let value = undefined;
69
- if (this.column.key && typeof this.row === "object") {
70
- value = rowGetValue(this.row as Record<any, any>, this.column.key)
71
- }
72
- this._emitFilter({
73
- include: true,
74
- row: this.row,
75
- value,
76
- column: this.column
77
- })
63
+ this._emitFilter({
64
+ include: false,
65
+ row: this.row,
66
+ value,
67
+ column: this.column,
68
+ });
69
+ };
70
+ _handleFilterFor = () => {
71
+ let value = undefined;
72
+ if (this.column.key && typeof this.row === "object") {
73
+ value = rowGetValue(this.row as Record<any, any>, this.column.key);
78
74
  }
79
- _displayTooltip = () => {
80
- let div = this.querySelector("div.cell-contents")
81
- let span = this.querySelector("span")
82
- if (div && span) {
83
- let spanBounds = span.getBoundingClientRect()
84
- let divBounds = div.getBoundingClientRect()
85
- if ((spanBounds.width * spanBounds.height) > (divBounds.width * divBounds.height)) {
86
- console.log("We need to show a tooltip witht he content because we are overflowing")
87
- this.overflow = this.getRenderedValue()
88
- this.updateComplete.then(() => {
89
- let tooltip = this.querySelector("spectric-tooltip")
90
- if (tooltip) {
91
- tooltip.showToolTip()
92
- }
93
- })
94
- } else {
95
- this.overflow = ""
96
- }
97
- }
75
+ this._emitFilter({
76
+ include: true,
77
+ row: this.row,
78
+ value,
79
+ column: this.column,
80
+ });
81
+ };
82
+ _displayTooltip = () => {
83
+ let div = this.querySelector("div.cell-contents");
84
+ let span = this.querySelector("span");
85
+ if (div && span) {
86
+ let spanBounds = span.getBoundingClientRect();
87
+ let divBounds = div.getBoundingClientRect();
88
+ if (
89
+ spanBounds.width * spanBounds.height >
90
+ divBounds.width * divBounds.height
91
+ ) {
92
+ console.log(
93
+ "We need to show a tooltip witht he content because we are overflowing"
94
+ );
95
+ this.overflow = this.getRenderedValue();
96
+ this.updateComplete.then(() => {
97
+ let tooltip = this.querySelector("spectric-tooltip");
98
+ if (tooltip) {
99
+ tooltip.showToolTip();
100
+ }
101
+ });
102
+ } else {
103
+ this.overflow = "";
104
+ }
98
105
  }
99
- getRenderedValue() {
100
- let rendered
101
- if (this.column.render) {
102
- rendered = this.column.render(this.row, this.index, this.table)
103
- } else if (this.column.key && typeof this.row === 'object') {
104
- rendered = rowGetValue(this.row as any, this.column.key)
105
- } else {
106
- rendered = html`error`
107
- }
108
- return rendered
106
+ };
107
+ getRenderedValue() {
108
+ let rendered;
109
+ if (this.column.render) {
110
+ rendered = this.column.render(this.row, this.index, this.table);
111
+ } else if (this.column.key && typeof this.row === "object") {
112
+ rendered = rowGetValue(this.row as any, this.column.key);
113
+ } else {
114
+ rendered = html`error`;
109
115
  }
110
- protected render(): unknown {
111
- let rendered = this.getRenderedValue()
112
- let classes = ["cell-contents"]
113
- if (this.column.filterable) {
114
- classes.push("filterable")
115
- }
116
-
117
- let filterButtons = cache(this.column.filterable
118
- ? html`<div class="table-cell-actions">
119
- <spectric-button @click=${this._handleFilterOut} size="tiny" variant="text" icon tooltip="Filter Out Value">-</spectric-button>
120
- <spectric-button @click=${this._handleFilterFor} size="tiny" variant="text" icon tooltip="Filter For Value">+</spectric-button></div>`
121
- : null
122
- )
123
- this.styleRules = {
124
- width: this.column.width ? this.column.width + "px" : null,
125
- whiteSpace: this.column.whiteSpace || null
126
- }
127
- return html`
128
- <td style=${styleMap(this.styleRules)} @mouseenter=${this._displayTooltip}>
129
- ${filterButtons}
130
- <div class=${classes.join(" ")}>${this.overflow ? html`<spectric-tooltip .text=${this.overflow}></spectric-tooltip>` : null}<span>${rendered}</span></div>
131
- </td>
132
- `
116
+ return rendered;
117
+ }
118
+ protected render(): unknown {
119
+ let rendered = this.getRenderedValue();
120
+ let classes = ["cell-contents"];
121
+ if (this.column.filterable) {
122
+ classes.push("filterable");
133
123
  }
134
124
 
125
+ let filterButtons = cache(
126
+ this.column.filterable
127
+ ? html`<div class="table-cell-actions">
128
+ <spectric-button
129
+ @click=${this._handleFilterOut}
130
+ size="tiny"
131
+ variant="text"
132
+ icon
133
+ tooltip="Filter Out Value"
134
+ >-</spectric-button
135
+ >
136
+ <spectric-button
137
+ @click=${this._handleFilterFor}
138
+ size="tiny"
139
+ variant="text"
140
+ icon
141
+ tooltip="Filter For Value"
142
+ >+</spectric-button
143
+ >
144
+ </div>`
145
+ : null
146
+ );
147
+ this.styleRules = {
148
+ width: this.column.width ? this.column.width + "px" : null,
149
+ whiteSpace: this.column.whiteSpace || null,
150
+ };
151
+ return html`
152
+ <td
153
+ style=${styleMap(this.styleRules)}
154
+ @mouseenter=${this._displayTooltip}
155
+ >
156
+ ${filterButtons}
157
+ <div class=${classes.join(" ")}>
158
+ ${this.overflow
159
+ ? html`<spectric-tooltip .text=${this.overflow}></spectric-tooltip>`
160
+ : null}<span>${rendered}</span>
161
+ </div>
162
+ </td>
163
+ `;
164
+ }
135
165
  }
136
166
 
137
167
  interface TableBodyEvents {
138
- 'filter': (event: CustomEvent<ColumnSettings<any>>) => void; //TODO filter events
168
+ filter: (event: CustomEvent<ColumnSettings<any>>) => void; //TODO filter events
139
169
  }
140
170
 
141
171
  declare global {
142
- interface HTMLElementTagNameMap {
143
- [TableCellElementTag]: HTMLElementTagWithEvents<TableCellElement<any>, TableBodyEvents>
144
-
172
+ interface HTMLElementTagNameMap {
173
+ [TableCellElementTag]: HTMLElementTagWithEvents<
174
+ TableCellElement<any>,
175
+ TableBodyEvents
176
+ >;
177
+ }
178
+ namespace JSX {
179
+ interface IntrinsicElements {
180
+ /**
181
+ * @see {@link DialogElement}
182
+ */
183
+ [TableCellElementTag]: ReactElementWithPropsAndEvents<
184
+ TableCellElement<any>,
185
+ CellProps<any>,
186
+ TableBodyEvents
187
+ >;
145
188
  }
189
+ }
190
+ namespace React {
146
191
  namespace JSX {
147
- interface IntrinsicElements {
148
- /**
149
- * @see {@link DialogElement}
150
- */
151
- [TableCellElementTag]: ReactElementWithPropsAndEvents<TableCellElement<any>, CellProps<any>, TableBodyEvents>;
152
- }
153
- }
154
- namespace React {
155
- namespace JSX {
156
- interface IntrinsicElements {
157
- /**
158
- * @see {@link DialogElement}
159
- */
160
- [TableCellElementTag]: ReactElementWithPropsAndEvents<TableCellElement<any>, CellProps<any>, TableBodyEvents>
161
- }
162
- }
192
+ interface IntrinsicElements {
193
+ /**
194
+ * @see {@link DialogElement}
195
+ */
196
+ [TableCellElementTag]: ReactElementWithPropsAndEvents<
197
+ TableCellElement<any>,
198
+ CellProps<any>,
199
+ TableBodyEvents
200
+ >;
201
+ }
163
202
  }
203
+ }
164
204
  }
165
205
 
166
-
167
206
  export const rowGetValue = (context: Record<string, any>, key: string) => {
168
- let path = key.split(".")
169
- let value = context[path[0]]
170
- if (value === null || value === undefined) {
171
- return value
172
- }
173
- if (path.length > 1) {
174
- value = rowGetValue(value, path.slice(1).join("."))
175
- }
176
- return value
177
- }
207
+ let path = key.split(".");
208
+ let value = context[path[0]];
209
+ if (value === null || value === undefined) {
210
+ return value;
211
+ }
212
+ if (path.length > 1) {
213
+ value = rowGetValue(value, path.slice(1).join("."));
214
+ }
215
+ return value;
216
+ };