Skip to content

Commit 81bead7

Browse files
chore(testing): Extends Coverage #2 (#6468)
* Add a test demonstrating how you can use extends to directly manage state as part of your baseClass * Extend Tests for @method decorator inheritance, super() calls, method override, and method composition. * Extend Tests for @prop and @State inheritance from base classes. * Extend Tests for extending Stencil-decorated classes with render() method inheritance. * Demonstrates base classes triggering host component updates via requestUpdate * Tests demonstrating lifecycle hooks living on the base class * Tests for multi-level lifecycle inheritance * Tests for ReactiveControllerHost pattern - composition based controllers with automatic lifecycle hooking * Run prettier * Tests for decorator conflicts - duplicate decorator names of the same type in inheritance chains Test Case #13 - Decorator Conflicts Features: - Duplicate @prop names (component overrides base) - Duplicate @State names (component overrides base) - Duplicate @method names (component overrides base) - Compiler precedence rules (component decorators take precedence) * Tests for event handling inheritance - @listen decorator inheritance Test Case #10 - Event Handling Inheritance Features: - Inherits base class window/document/host listeners - Handles child class window/document/host listeners - Child override event handler takes precedence over base - Handles event bubbling correctly - Tracks events in combined event log * Tests for @watch decorator inheritance Test Case #11 - Watch Decorator Inheritance Features: - Inherits base class @watch decorators - Handles child class @watch decorators - Executes watch handlers in correct order (base first, then child) - Child override watch handler takes precedence over base - Reactive property chains work correctly - Tracks watch calls in combined watch log * Tests for mixed decorator types - different decorator types with same name Test Case #18 - Mixed Decorator Types Features: - @prop in Base, @State in Component (same name) - @State in Base, @prop in Component (same name) - @method in Base, @prop in Component (same name) - Component decorator type takes precedence - Runtime behavior verification * Tests for inheritance-based scaling with multiple components and controllers Test Case #15 - Inheritance-Based Scaling Features: - 3 components (TextInput, RadioGroup, CheckboxGroup) using inheritance - 2 controllers (ValidationController, FocusController) via inheritance - Lifecycle methods called correctly - Validation triggers on blur - Focus tracking works - Controllers use inheritance pattern * Tests for composition-based scaling with multiple components and controllers Test Case #16 - Composition-Based Scaling Features: - 3 components (TextInput, RadioGroup, CheckboxGroup) using composition - 2 controllers (ValidationController, FocusController) via composition - Lifecycle methods called automatically via ReactiveControllerHost - Validation triggers on blur - Focus tracking works - Controllers are properly composed (not inherited) * Apply prettier formatting & tweak test case #s * Remove the duplicate method as it was causing types error --------- Co-authored-by: Paul Visciano <[email protected]>
1 parent 3771af0 commit 81bead7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+5043
-3
lines changed

test/wdio/ts-target/components.d.ts

Lines changed: 556 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { Component, h, State, Element, Event, EventEmitter } from '@stencil/core';
2+
import { ReactiveControllerHost } from './reactive-controller-host.js';
3+
import { ValidationController } from './validation-controller.js';
4+
import { FocusController } from './focus-controller.js';
5+
6+
@Component({
7+
tag: 'composition-checkbox-group',
8+
})
9+
export class CheckboxGroupCmp extends ReactiveControllerHost {
10+
@Element() el!: HTMLElement;
11+
@State() values: string[] = [];
12+
@State() helperText: string = 'Select at least one option';
13+
14+
@Event() valueChange!: EventEmitter<string[]>;
15+
16+
// Controllers via composition
17+
private validation = new ValidationController(this);
18+
private focus = new FocusController(this);
19+
20+
private inputId = `checkbox-group-${Math.random().toString(36).substr(2, 9)}`;
21+
private helperTextId = `${this.inputId}-helper-text`;
22+
private errorTextId = `${this.inputId}-error-text`;
23+
24+
componentWillLoad() {
25+
super.componentWillLoad(); // Call base class to trigger controllers
26+
// Set up validation callback
27+
this.validation.setValidationCallback((vals: string[]) => {
28+
if (!vals || vals.length === 0) {
29+
return 'Please select at least one option';
30+
}
31+
return undefined;
32+
});
33+
}
34+
35+
componentDidLoad() {
36+
super.componentDidLoad(); // Call base class to trigger controllers
37+
}
38+
39+
disconnectedCallback() {
40+
super.disconnectedCallback(); // Call base class to trigger controllers
41+
}
42+
43+
private handleChange = (e: Event) => {
44+
const checkbox = e.target as HTMLInputElement;
45+
const value = checkbox.value;
46+
47+
if (checkbox.checked) {
48+
this.values = [...this.values, value];
49+
} else {
50+
this.values = this.values.filter((v) => v !== value);
51+
}
52+
53+
this.valueChange.emit(this.values);
54+
this.validation.validate(this.values);
55+
};
56+
57+
private handleFocus = () => {
58+
this.focus.handleFocus();
59+
};
60+
61+
private handleBlur = () => {
62+
this.focus.handleBlur();
63+
this.validation.handleBlur(this.values);
64+
};
65+
66+
render() {
67+
const focusState = this.focus.getFocusState();
68+
const validationData = this.validation.getValidationMessageData(this.helperTextId, this.errorTextId);
69+
70+
return (
71+
<div class="checkbox-group-container">
72+
<label>Select Options</label>
73+
<div class="checkbox-group" tabindex="0" onFocus={this.handleFocus} onBlur={this.handleBlur}>
74+
<label>
75+
<input
76+
type="checkbox"
77+
name={this.inputId}
78+
value="option1"
79+
checked={this.values.includes('option1')}
80+
onChange={this.handleChange}
81+
/>
82+
Option 1
83+
</label>
84+
<label>
85+
<input
86+
type="checkbox"
87+
name={this.inputId}
88+
value="option2"
89+
checked={this.values.includes('option2')}
90+
onChange={this.handleChange}
91+
/>
92+
Option 2
93+
</label>
94+
<label>
95+
<input
96+
type="checkbox"
97+
name={this.inputId}
98+
value="option3"
99+
checked={this.values.includes('option3')}
100+
onChange={this.handleChange}
101+
/>
102+
Option 3
103+
</label>
104+
</div>
105+
{validationData.hasError && (
106+
<div class="validation-message">
107+
<div id={validationData.errorTextId} class="error-text">
108+
{validationData.errorMessage}
109+
</div>
110+
</div>
111+
)}
112+
<div class="focus-info">
113+
Focused: {focusState.isFocused ? 'Yes' : 'No'} | Focus Count: {focusState.focusCount} | Blur Count:{' '}
114+
{focusState.blurCount}
115+
</div>
116+
</div>
117+
);
118+
}
119+
}

0 commit comments

Comments
 (0)