@openwebf/webf 0.22.8 → 0.22.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwebf/webf",
3
- "version": "0.22.8",
3
+ "version": "0.22.9",
4
4
  "description": "Command line tools for WebF",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -40,6 +40,11 @@ export interface <%= className %>Props {
40
40
  <%= propName %>?: (event: <%= getEventType(prop.type) %>) => void;
41
41
 
42
42
  <% }); %>
43
+ /**
44
+ * HTML id attribute
45
+ */
46
+ id?: string;
47
+
43
48
  /**
44
49
  * Additional CSS styles
45
50
  */
@@ -7,6 +7,9 @@ export type <%= className %>Props = {
7
7
  '<%= propName %>': <%= generateReturnType(prop.type) %>;
8
8
  <% } %>
9
9
  <% }); %>
10
+ 'id'?: string;
11
+ 'class'?: string;
12
+ 'style'?: string | Record<string, any>;
10
13
  }
11
14
 
12
15
  export interface <%= className %>Element {
@@ -1,6 +1,6 @@
1
1
  import { generateReactComponent } from '../src/react';
2
2
  import { IDLBlob } from '../src/IDLBlob';
3
- import { ClassObject, ClassObjectKind } from '../src/declaration';
3
+ import { ClassObject, ClassObjectKind, PropsDeclaration } from '../src/declaration';
4
4
 
5
5
  // Import the toWebFTagName function for testing
6
6
  import { toWebFTagName } from '../src/react';
@@ -130,5 +130,66 @@ describe('React Generator', () => {
130
130
  // From src/lib/src/html/shimmer to src/utils: ../../../../utils
131
131
  expect(result).toContain('import { createWebFComponent, WebFElementWithMethods } from "../../../../utils/createWebFComponent"');
132
132
  });
133
+
134
+ it('should include standard HTML props (id, className, style) in component interface', () => {
135
+ const blob = new IDLBlob('/test/source', '/test/target', 'TestComponent', 'test', '');
136
+
137
+ const properties = new ClassObject();
138
+ properties.name = 'TestComponentProperties';
139
+ properties.kind = ClassObjectKind.interface;
140
+ blob.objects = [properties];
141
+
142
+ const result = generateReactComponent(blob);
143
+
144
+ // Should include standard HTML props
145
+ expect(result).toContain('id?: string;');
146
+ expect(result).toContain('style?: React.CSSProperties;');
147
+ expect(result).toContain('children?: React.ReactNode;');
148
+ expect(result).toContain('className?: string;');
149
+
150
+ // Props should have proper JSDoc comments
151
+ expect(result).toMatch(/\/\*\*\s*\n\s*\*\s*HTML id attribute\s*\n\s*\*\/\s*\n\s*id\?: string;/);
152
+ expect(result).toMatch(/\/\*\*\s*\n\s*\*\s*Additional CSS styles\s*\n\s*\*\/\s*\n\s*style\?: React\.CSSProperties;/);
153
+ expect(result).toMatch(/\/\*\*\s*\n\s*\*\s*Children elements\s*\n\s*\*\/\s*\n\s*children\?: React\.ReactNode;/);
154
+ expect(result).toMatch(/\/\*\*\s*\n\s*\*\s*Additional CSS class names\s*\n\s*\*\/\s*\n\s*className\?: string;/);
155
+ });
156
+
157
+ it('should include standard HTML props even when component has custom properties', () => {
158
+ const blob = new IDLBlob('/test/source', '/test/target', 'TestComponent', 'test', '');
159
+
160
+ const properties = new ClassObject();
161
+ properties.name = 'TestComponentProperties';
162
+ properties.kind = ClassObjectKind.interface;
163
+ const titleProp = new PropsDeclaration();
164
+ titleProp.name = 'title';
165
+ titleProp.type = { value: 'dom_string' };
166
+ titleProp.optional = false;
167
+ titleProp.documentation = 'The component title';
168
+ titleProp.readonly = false;
169
+ titleProp.typeMode = {};
170
+
171
+ const disabledProp = new PropsDeclaration();
172
+ disabledProp.name = 'disabled';
173
+ disabledProp.type = { value: 'boolean' };
174
+ disabledProp.optional = true;
175
+ disabledProp.documentation = 'Whether the component is disabled';
176
+ disabledProp.readonly = false;
177
+ disabledProp.typeMode = {};
178
+
179
+ properties.props = [titleProp, disabledProp];
180
+ blob.objects = [properties];
181
+
182
+ const result = generateReactComponent(blob);
183
+
184
+ // Should include custom props (dom_string is not converted in raw output)
185
+ expect(result).toContain('title: dom_string;');
186
+ expect(result).toContain('disabled?: boolean;');
187
+
188
+ // And still include standard HTML props
189
+ expect(result).toContain('id?: string;');
190
+ expect(result).toContain('style?: React.CSSProperties;');
191
+ expect(result).toContain('children?: React.ReactNode;');
192
+ expect(result).toContain('className?: string;');
193
+ });
133
194
  });
134
195
  });
@@ -0,0 +1,190 @@
1
+ import { generateReactComponent } from '../src/react';
2
+ import { generateVueTypings } from '../src/vue';
3
+ import { IDLBlob } from '../src/IDLBlob';
4
+ import { ClassObject, ClassObjectKind, PropsDeclaration } from '../src/declaration';
5
+
6
+ describe('Standard HTML Props Generation', () => {
7
+ describe('React Components', () => {
8
+ it('should generate id prop in the correct position within the interface', () => {
9
+ const blob = new IDLBlob('/test/source', '/test/target', 'TestButton', 'test', '');
10
+
11
+ const properties = new ClassObject();
12
+ properties.name = 'TestButtonProperties';
13
+ properties.kind = ClassObjectKind.interface;
14
+ const labelProp = new PropsDeclaration();
15
+ labelProp.name = 'label';
16
+ labelProp.type = { value: 'dom_string' };
17
+ labelProp.optional = false;
18
+ labelProp.readonly = false;
19
+ labelProp.typeMode = {};
20
+
21
+ const variantProp = new PropsDeclaration();
22
+ variantProp.name = 'variant';
23
+ variantProp.type = { value: 'dom_string' };
24
+ variantProp.optional = true;
25
+ variantProp.readonly = false;
26
+ variantProp.typeMode = {};
27
+
28
+ properties.props = [labelProp, variantProp];
29
+
30
+ const events = new ClassObject();
31
+ events.name = 'TestButtonEvents';
32
+ events.kind = ClassObjectKind.interface;
33
+ const clickProp = new PropsDeclaration();
34
+ clickProp.name = 'click';
35
+ clickProp.type = { value: 'Event', isArray: false };
36
+ clickProp.optional = true;
37
+ clickProp.readonly = false;
38
+ clickProp.typeMode = {};
39
+
40
+ events.props = [clickProp];
41
+
42
+ blob.objects = [properties, events];
43
+
44
+ const result = generateReactComponent(blob);
45
+
46
+ // Verify the props interface structure - extract full content including newlines
47
+ const propsStart = result.indexOf('export interface TestButtonProps {');
48
+ const propsEnd = result.indexOf('}', propsStart) + 1;
49
+ const propsContent = result.substring(propsStart, propsEnd);
50
+
51
+ // Verify order: custom props, event handlers, then standard HTML props
52
+ const labelIndex = propsContent.indexOf('label: dom_string;');
53
+ const variantIndex = propsContent.indexOf('variant?: dom_string;');
54
+ const onClickIndex = propsContent.indexOf('onClick?: (event: Event) => void;');
55
+ const idIndex = propsContent.indexOf('id?: string;');
56
+ const styleIndex = propsContent.indexOf('style?: React.CSSProperties;');
57
+ const childrenIndex = propsContent.indexOf('children?: React.ReactNode;');
58
+ const classNameIndex = propsContent.indexOf('className?: string;');
59
+
60
+ // All props should exist
61
+ expect(labelIndex).toBeGreaterThan(-1);
62
+ expect(variantIndex).toBeGreaterThan(-1);
63
+ expect(onClickIndex).toBeGreaterThan(-1);
64
+ expect(idIndex).toBeGreaterThan(-1);
65
+ expect(styleIndex).toBeGreaterThan(-1);
66
+ expect(childrenIndex).toBeGreaterThan(-1);
67
+ expect(classNameIndex).toBeGreaterThan(-1);
68
+
69
+ // Verify order
70
+ expect(labelIndex).toBeLessThan(variantIndex);
71
+ expect(variantIndex).toBeLessThan(onClickIndex);
72
+ expect(onClickIndex).toBeLessThan(idIndex);
73
+ expect(idIndex).toBeLessThan(styleIndex);
74
+ expect(styleIndex).toBeLessThan(childrenIndex);
75
+ expect(childrenIndex).toBeLessThan(classNameIndex);
76
+ });
77
+
78
+ it('should properly type the standard props', () => {
79
+ const blob = new IDLBlob('/test/source', '/test/target', 'SimpleComponent', 'test', '');
80
+
81
+ const properties = new ClassObject();
82
+ properties.name = 'SimpleComponentProperties';
83
+ properties.kind = ClassObjectKind.interface;
84
+ blob.objects = [properties];
85
+
86
+ const result = generateReactComponent(blob);
87
+
88
+ // Verify exact type definitions
89
+ expect(result).toMatch(/id\?: string;/);
90
+ expect(result).toMatch(/style\?: React\.CSSProperties;/);
91
+ expect(result).toMatch(/children\?: React\.ReactNode;/);
92
+ expect(result).toMatch(/className\?: string;/);
93
+ });
94
+ });
95
+
96
+ describe('Vue Components', () => {
97
+ it('should generate standard HTML props with correct Vue naming conventions', () => {
98
+ const blob = new IDLBlob('/test/source', '/test/target', 'VueButton', 'test', '');
99
+
100
+ const properties = new ClassObject();
101
+ properties.name = 'VueButtonProperties';
102
+ properties.kind = ClassObjectKind.interface;
103
+ const labelProp = new PropsDeclaration();
104
+ labelProp.name = 'label';
105
+ labelProp.type = { value: 'dom_string' };
106
+ labelProp.optional = false;
107
+ labelProp.readonly = false;
108
+ labelProp.typeMode = {};
109
+
110
+ const isDisabledProp = new PropsDeclaration();
111
+ isDisabledProp.name = 'isDisabled';
112
+ isDisabledProp.type = { value: 'boolean' };
113
+ isDisabledProp.optional = true;
114
+ isDisabledProp.readonly = false;
115
+ isDisabledProp.typeMode = {};
116
+
117
+ properties.props = [labelProp, isDisabledProp];
118
+
119
+ blob.objects = [properties];
120
+
121
+ const result = generateVueTypings([blob]);
122
+
123
+ // Verify Props type includes custom and standard props - extract full content
124
+ const propsStart = result.indexOf('export type VueButtonProps = {');
125
+ const propsEnd = result.indexOf('}', propsStart) + 1;
126
+ const propsContent = result.substring(propsStart, propsEnd);
127
+
128
+ // Custom props should be kebab-case (dom_string is not converted)
129
+ expect(propsContent).toContain("'label': dom_string;");
130
+ expect(propsContent).toContain("'is-disabled'?: boolean;");
131
+
132
+ // Standard HTML props
133
+ expect(propsContent).toContain("'id'?: string;");
134
+ expect(propsContent).toContain("'class'?: string;");
135
+ expect(propsContent).toContain("'style'?: string | Record<string, any>;");
136
+ });
137
+
138
+ it('should handle Vue style prop with both string and object types', () => {
139
+ const blob = new IDLBlob('/test/source', '/test/target', 'StyledComponent', 'test', '');
140
+
141
+ const properties = new ClassObject();
142
+ properties.name = 'StyledComponentProperties';
143
+ properties.kind = ClassObjectKind.interface;
144
+ blob.objects = [properties];
145
+
146
+ const result = generateVueTypings([blob]);
147
+
148
+ // Vue style prop should accept both string and object
149
+ expect(result).toMatch(/'style'\?: string \| Record<string, any>;/);
150
+ });
151
+ });
152
+
153
+ describe('Cross-framework consistency', () => {
154
+ it('should generate equivalent props for both React and Vue', () => {
155
+ const blob = new IDLBlob('/test/source', '/test/target', 'CrossFrameworkComponent', 'test', '');
156
+
157
+ const properties = new ClassObject();
158
+ properties.name = 'CrossFrameworkComponentProperties';
159
+ properties.kind = ClassObjectKind.interface;
160
+ const titleProp = new PropsDeclaration();
161
+ titleProp.name = 'title';
162
+ titleProp.type = { value: 'dom_string' };
163
+ titleProp.optional = false;
164
+ titleProp.readonly = false;
165
+ titleProp.typeMode = {};
166
+
167
+ properties.props = [titleProp];
168
+ blob.objects = [properties];
169
+
170
+ const reactResult = generateReactComponent(blob);
171
+ const vueResult = generateVueTypings([blob]);
172
+
173
+ // Both should have id prop
174
+ expect(reactResult).toContain('id?: string;');
175
+ expect(vueResult).toContain("'id'?: string;");
176
+
177
+ // Both should have style prop (with appropriate types)
178
+ expect(reactResult).toContain('style?: React.CSSProperties;');
179
+ expect(vueResult).toContain("'style'?: string | Record<string, any>;");
180
+
181
+ // React has className, Vue has class
182
+ expect(reactResult).toContain('className?: string;');
183
+ expect(vueResult).toContain("'class'?: string;");
184
+
185
+ // React has children, Vue uses slots (not in props)
186
+ expect(reactResult).toContain('children?: React.ReactNode;');
187
+ expect(vueResult).not.toContain('children');
188
+ });
189
+ });
190
+ });
@@ -0,0 +1,157 @@
1
+ import { generateVueTypings } from '../src/vue';
2
+ import { IDLBlob } from '../src/IDLBlob';
3
+ import { ClassObject, ClassObjectKind, PropsDeclaration } from '../src/declaration';
4
+
5
+ describe('Vue Generator', () => {
6
+ describe('generateVueTypings', () => {
7
+ it('should generate Vue component types with standard HTML props', () => {
8
+ const blob = new IDLBlob('/test/source', '/test/target', 'TestComponent', 'test', '');
9
+
10
+ const properties = new ClassObject();
11
+ properties.name = 'TestComponentProperties';
12
+ properties.kind = ClassObjectKind.interface;
13
+ blob.objects = [properties];
14
+
15
+ const result = generateVueTypings([blob]);
16
+
17
+ // Should include standard HTML props in Props type
18
+ expect(result).toContain("'id'?: string;");
19
+ expect(result).toContain("'class'?: string;");
20
+ expect(result).toContain("'style'?: string | Record<string, any>;");
21
+
22
+ // Should generate proper type exports
23
+ expect(result).toContain('export type TestComponentProps = {');
24
+ expect(result).toContain('export interface TestComponentElement {');
25
+ expect(result).toContain('export type TestComponentEvents = {');
26
+ });
27
+
28
+ it('should include standard HTML props along with custom properties', () => {
29
+ const blob = new IDLBlob('/test/source', '/test/target', 'TestComponent', 'test', '');
30
+
31
+ const properties = new ClassObject();
32
+ properties.name = 'TestComponentProperties';
33
+ properties.kind = ClassObjectKind.interface;
34
+ const titleProp = new PropsDeclaration();
35
+ titleProp.name = 'title';
36
+ titleProp.type = { value: 'dom_string' };
37
+ titleProp.optional = false;
38
+ titleProp.documentation = 'The component title';
39
+ titleProp.readonly = false;
40
+ titleProp.typeMode = {};
41
+
42
+ const isActiveProp = new PropsDeclaration();
43
+ isActiveProp.name = 'isActive';
44
+ isActiveProp.type = { value: 'boolean' };
45
+ isActiveProp.optional = true;
46
+ isActiveProp.documentation = 'Whether the component is active';
47
+ isActiveProp.readonly = false;
48
+ isActiveProp.typeMode = {};
49
+
50
+ properties.props = [titleProp, isActiveProp];
51
+ blob.objects = [properties];
52
+
53
+ const result = generateVueTypings([blob]);
54
+
55
+ // Should include custom props with kebab-case (dom_string is not converted in type definitions)
56
+ expect(result).toContain("'title': dom_string;");
57
+ expect(result).toContain("'is-active'?: boolean;");
58
+
59
+ // And still include standard HTML props
60
+ expect(result).toContain("'id'?: string;");
61
+ expect(result).toContain("'class'?: string;");
62
+ expect(result).toContain("'style'?: string | Record<string, any>;");
63
+ });
64
+
65
+ it('should generate proper Vue component declarations', () => {
66
+ const blob = new IDLBlob('/test/source', '/test/target', 'WebFListView', 'test', '');
67
+
68
+ const properties = new ClassObject();
69
+ properties.name = 'WebFListViewProperties';
70
+ properties.kind = ClassObjectKind.interface;
71
+ blob.objects = [properties];
72
+
73
+ const result = generateVueTypings([blob]);
74
+
75
+ // Should generate proper component declarations
76
+ expect(result).toContain("declare module 'vue' {");
77
+ expect(result).toContain("interface GlobalComponents {");
78
+ expect(result).toContain("'web-f-list-view': DefineCustomElement<");
79
+ expect(result).toContain("WebFListViewProps,");
80
+ expect(result).toContain("WebFListViewEvents");
81
+ });
82
+
83
+ it('should handle multiple components', () => {
84
+ const blob1 = new IDLBlob('/test/source', '/test/target', 'ComponentOne', 'test', '');
85
+ const properties1 = new ClassObject();
86
+ properties1.name = 'ComponentOneProperties';
87
+ properties1.kind = ClassObjectKind.interface;
88
+ blob1.objects = [properties1];
89
+
90
+ const blob2 = new IDLBlob('/test/source', '/test/target', 'ComponentTwo', 'test', '');
91
+ const properties2 = new ClassObject();
92
+ properties2.name = 'ComponentTwoProperties';
93
+ properties2.kind = ClassObjectKind.interface;
94
+ blob2.objects = [properties2];
95
+
96
+ const result = generateVueTypings([blob1, blob2]);
97
+
98
+ // Should include both components
99
+ expect(result).toContain('export type ComponentOneProps = {');
100
+ expect(result).toContain('export type ComponentTwoProps = {');
101
+
102
+ // Both should have standard HTML props
103
+ const componentOneMatch = result.match(/export type ComponentOneProps = \{[^}]+\}/);
104
+ const componentTwoMatch = result.match(/export type ComponentTwoProps = \{[^}]+\}/);
105
+
106
+ // Extract content including newlines
107
+ const componentOneSection = result.substring(result.indexOf('export type ComponentOneProps'), result.indexOf('export interface ComponentOneElement'));
108
+ const componentTwoSection = result.substring(result.indexOf('export type ComponentTwoProps'), result.indexOf('export interface ComponentTwoElement'));
109
+
110
+ expect(componentOneSection).toContain("'id'?: string;");
111
+ expect(componentTwoSection).toContain("'id'?: string;");
112
+ });
113
+
114
+ it('should handle components with events', () => {
115
+ const blob = new IDLBlob('/test/source', '/test/target', 'TestComponent', 'test', '');
116
+
117
+ const properties = new ClassObject();
118
+ properties.name = 'TestComponentProperties';
119
+ properties.kind = ClassObjectKind.interface;
120
+
121
+ const events = new ClassObject();
122
+ events.name = 'TestComponentEvents';
123
+ events.kind = ClassObjectKind.interface;
124
+ const closeProp = new PropsDeclaration();
125
+ closeProp.name = 'close';
126
+ closeProp.type = { value: 'Event', isArray: false };
127
+ closeProp.optional = true;
128
+ closeProp.documentation = 'Close event';
129
+ closeProp.readonly = false;
130
+ closeProp.typeMode = {};
131
+
132
+ const refreshProp = new PropsDeclaration();
133
+ refreshProp.name = 'refresh';
134
+ refreshProp.type = { value: 'CustomEvent', isArray: false };
135
+ refreshProp.optional = true;
136
+ refreshProp.documentation = 'Refresh event';
137
+ refreshProp.readonly = false;
138
+ refreshProp.typeMode = {};
139
+
140
+ events.props = [closeProp, refreshProp];
141
+
142
+ blob.objects = [properties, events];
143
+
144
+ const result = generateVueTypings([blob]);
145
+
146
+ // Should include event types
147
+ expect(result).toContain('export type TestComponentEvents = {');
148
+ expect(result).toContain('close?: Event;');
149
+ expect(result).toContain('refresh?: CustomEvent;');
150
+
151
+ // Props should still have standard HTML props
152
+ expect(result).toContain("'id'?: string;");
153
+ expect(result).toContain("'class'?: string;");
154
+ expect(result).toContain("'style'?: string | Record<string, any>;");
155
+ });
156
+ });
157
+ });