@pyreon/attrs 0.11.4 → 0.11.6
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/README.md +18 -13
- package/package.json +22 -22
- package/src/__tests__/attrs.test.ts +177 -177
- package/src/__tests__/attrsHoc.test.ts +39 -39
- package/src/__tests__/utils.test.ts +69 -69
- package/src/attrs.ts +13 -13
- package/src/hoc/attrsHoc.ts +4 -4
- package/src/hoc/index.ts +1 -1
- package/src/index.ts +9 -9
- package/src/init.ts +7 -7
- package/src/isAttrsComponent.ts +2 -2
- package/src/types/AttrsComponent.ts +6 -6
- package/src/types/InitAttrsComponent.ts +3 -3
- package/src/types/config.ts +1 -1
- package/src/types/configuration.ts +1 -1
- package/src/types/hoc.ts +1 -1
- package/src/types/utils.ts +1 -1
- package/src/utils/attrs.ts +1 -1
- package/src/utils/chaining.ts +2 -2
- package/src/utils/compose.ts +1 -1
- package/src/utils/statics.ts +1 -1
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import attrsComponent from
|
|
2
|
-
import attrs from
|
|
3
|
-
import isAttrsComponent from
|
|
1
|
+
import attrsComponent from '../attrs'
|
|
2
|
+
import attrs from '../init'
|
|
3
|
+
import isAttrsComponent from '../isAttrsComponent'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Simple base component for testing.
|
|
7
7
|
* Returns a VNode-like object so we can inspect the final props.
|
|
8
8
|
*/
|
|
9
9
|
const BaseComponent = (props: any) => ({
|
|
10
|
-
type:
|
|
11
|
-
props: { ...props,
|
|
10
|
+
type: 'div',
|
|
11
|
+
props: { ...props, 'data-testid': 'base' },
|
|
12
12
|
children: props.children ?? props.label ?? null,
|
|
13
13
|
key: null,
|
|
14
14
|
})
|
|
@@ -22,201 +22,201 @@ const renderProps = (Component: any, props: Record<string, any> = {}) => {
|
|
|
22
22
|
// --------------------------------------------------------
|
|
23
23
|
// attrs() initialization
|
|
24
24
|
// --------------------------------------------------------
|
|
25
|
-
describe(
|
|
26
|
-
it(
|
|
27
|
-
const Component = attrs({ name:
|
|
25
|
+
describe('attrs initialization', () => {
|
|
26
|
+
it('should create an attrs component from a base component', () => {
|
|
27
|
+
const Component = attrs({ name: 'TestComponent', component: BaseComponent })
|
|
28
28
|
expect(Component).toBeDefined()
|
|
29
29
|
expect(Component.IS_ATTRS).toBe(true)
|
|
30
|
-
expect(Component.displayName).toBe(
|
|
30
|
+
expect(Component.displayName).toBe('TestComponent')
|
|
31
31
|
})
|
|
32
32
|
|
|
33
|
-
it(
|
|
34
|
-
expect(() => attrs({ name:
|
|
33
|
+
it('should throw when component is missing (dev mode)', () => {
|
|
34
|
+
expect(() => attrs({ name: 'Test', component: undefined as any })).toThrow()
|
|
35
35
|
})
|
|
36
36
|
|
|
37
|
-
it(
|
|
37
|
+
it('should throw when name is missing (dev mode)', () => {
|
|
38
38
|
expect(() => attrs({ name: undefined as any, component: BaseComponent })).toThrow()
|
|
39
39
|
})
|
|
40
40
|
|
|
41
|
-
it(
|
|
42
|
-
const Component = attrs({ name:
|
|
43
|
-
const result = renderProps(Component, { label:
|
|
44
|
-
expect(result.label).toBe(
|
|
41
|
+
it('should render the wrapped component', () => {
|
|
42
|
+
const Component = attrs({ name: 'Test', component: BaseComponent })
|
|
43
|
+
const result = renderProps(Component, { label: 'Hello' })
|
|
44
|
+
expect(result.label).toBe('Hello')
|
|
45
45
|
})
|
|
46
46
|
|
|
47
|
-
it(
|
|
48
|
-
const Component = attrs({ name:
|
|
47
|
+
it('should add data-attrs in development mode', () => {
|
|
48
|
+
const Component = attrs({ name: 'MyComponent', component: BaseComponent })
|
|
49
49
|
const result = renderProps(Component)
|
|
50
|
-
expect(result[
|
|
50
|
+
expect(result['data-attrs']).toBe('MyComponent')
|
|
51
51
|
})
|
|
52
52
|
})
|
|
53
53
|
|
|
54
54
|
// --------------------------------------------------------
|
|
55
55
|
// .attrs() chaining
|
|
56
56
|
// --------------------------------------------------------
|
|
57
|
-
describe(
|
|
58
|
-
it(
|
|
57
|
+
describe('.attrs() chaining', () => {
|
|
58
|
+
it('should apply default attrs to the component', () => {
|
|
59
59
|
const Component = attrs({
|
|
60
|
-
name:
|
|
60
|
+
name: 'Test',
|
|
61
61
|
component: BaseComponent,
|
|
62
|
-
}).attrs(() => ({ label:
|
|
62
|
+
}).attrs(() => ({ label: 'Default Label' }))
|
|
63
63
|
|
|
64
64
|
const result = renderProps(Component)
|
|
65
|
-
expect(result.label).toBe(
|
|
65
|
+
expect(result.label).toBe('Default Label')
|
|
66
66
|
})
|
|
67
67
|
|
|
68
|
-
it(
|
|
68
|
+
it('should allow props to override default attrs', () => {
|
|
69
69
|
const Component = attrs({
|
|
70
|
-
name:
|
|
70
|
+
name: 'Test',
|
|
71
71
|
component: BaseComponent,
|
|
72
|
-
}).attrs(() => ({ label:
|
|
72
|
+
}).attrs(() => ({ label: 'Default' }))
|
|
73
73
|
|
|
74
|
-
const result = renderProps(Component, { label:
|
|
75
|
-
expect(result.label).toBe(
|
|
74
|
+
const result = renderProps(Component, { label: 'Override' })
|
|
75
|
+
expect(result.label).toBe('Override')
|
|
76
76
|
})
|
|
77
77
|
|
|
78
|
-
it(
|
|
78
|
+
it('should support multiple .attrs() chains', () => {
|
|
79
79
|
const Component = attrs({
|
|
80
|
-
name:
|
|
80
|
+
name: 'Test',
|
|
81
81
|
component: BaseComponent,
|
|
82
82
|
})
|
|
83
|
-
.attrs(() => ({
|
|
84
|
-
.attrs(() => ({
|
|
83
|
+
.attrs(() => ({ 'data-first': 'yes' }))
|
|
84
|
+
.attrs(() => ({ 'data-second': 'yes' }))
|
|
85
85
|
|
|
86
86
|
const result = renderProps(Component)
|
|
87
|
-
expect(result[
|
|
88
|
-
expect(result[
|
|
87
|
+
expect(result['data-first']).toBe('yes')
|
|
88
|
+
expect(result['data-second']).toBe('yes')
|
|
89
89
|
})
|
|
90
90
|
|
|
91
|
-
it(
|
|
91
|
+
it('should pass current props to attrs callback', () => {
|
|
92
92
|
const Component = attrs({
|
|
93
|
-
name:
|
|
93
|
+
name: 'Test',
|
|
94
94
|
component: BaseComponent,
|
|
95
95
|
}).attrs((props: any) => ({
|
|
96
|
-
|
|
96
|
+
'data-variant': props.variant === 'primary' ? 'is-primary' : 'is-default',
|
|
97
97
|
}))
|
|
98
98
|
|
|
99
|
-
const result = renderProps(Component, { variant:
|
|
100
|
-
expect(result[
|
|
99
|
+
const result = renderProps(Component, { variant: 'primary' })
|
|
100
|
+
expect(result['data-variant']).toBe('is-primary')
|
|
101
101
|
})
|
|
102
102
|
|
|
103
|
-
it(
|
|
103
|
+
it('should support object-based attrs', () => {
|
|
104
104
|
const Component = attrs({
|
|
105
|
-
name:
|
|
105
|
+
name: 'Test',
|
|
106
106
|
component: BaseComponent,
|
|
107
|
-
}).attrs({ label:
|
|
107
|
+
}).attrs({ label: 'Static Label' })
|
|
108
108
|
|
|
109
109
|
const result = renderProps(Component)
|
|
110
|
-
expect(result.label).toBe(
|
|
110
|
+
expect(result.label).toBe('Static Label')
|
|
111
111
|
})
|
|
112
112
|
|
|
113
|
-
it(
|
|
113
|
+
it('should support priority attrs', () => {
|
|
114
114
|
const Component = attrs({
|
|
115
|
-
name:
|
|
115
|
+
name: 'Test',
|
|
116
116
|
component: BaseComponent,
|
|
117
117
|
})
|
|
118
|
-
.attrs(() => ({ label:
|
|
119
|
-
.attrs(() => ({ label:
|
|
118
|
+
.attrs(() => ({ label: 'Normal' }))
|
|
119
|
+
.attrs(() => ({ label: 'Priority' }), { priority: true })
|
|
120
120
|
|
|
121
121
|
// Priority attrs have lower precedence than normal attrs
|
|
122
122
|
const result = renderProps(Component)
|
|
123
|
-
expect(result.label).toBe(
|
|
123
|
+
expect(result.label).toBe('Normal')
|
|
124
124
|
})
|
|
125
125
|
|
|
126
|
-
it(
|
|
126
|
+
it('should support filter option to remove attrs from final props', () => {
|
|
127
127
|
const Component = attrs({
|
|
128
|
-
name:
|
|
128
|
+
name: 'Test',
|
|
129
129
|
component: BaseComponent,
|
|
130
|
-
}).attrs(() => ({ label:
|
|
131
|
-
filter: [
|
|
130
|
+
}).attrs(() => ({ label: 'Visible' }), {
|
|
131
|
+
filter: ['data-internal'],
|
|
132
132
|
})
|
|
133
133
|
|
|
134
134
|
const result = renderProps(Component, {
|
|
135
|
-
|
|
136
|
-
label:
|
|
135
|
+
'data-internal': 'secret',
|
|
136
|
+
label: 'test',
|
|
137
137
|
})
|
|
138
|
-
expect(result[
|
|
138
|
+
expect(result['data-internal']).toBeUndefined()
|
|
139
139
|
})
|
|
140
140
|
})
|
|
141
141
|
|
|
142
142
|
// --------------------------------------------------------
|
|
143
143
|
// .config() chaining
|
|
144
144
|
// --------------------------------------------------------
|
|
145
|
-
describe(
|
|
146
|
-
it(
|
|
147
|
-
const Original = attrs({ name:
|
|
145
|
+
describe('.config() chaining', () => {
|
|
146
|
+
it('should return a new component instance', () => {
|
|
147
|
+
const Original = attrs({ name: 'Test', component: BaseComponent })
|
|
148
148
|
const Configured = Original.config({})
|
|
149
149
|
expect(Configured).not.toBe(Original)
|
|
150
150
|
expect(Configured.IS_ATTRS).toBe(true)
|
|
151
151
|
})
|
|
152
152
|
|
|
153
|
-
it(
|
|
154
|
-
const Original = attrs({ name:
|
|
155
|
-
const Renamed = Original.config({ name:
|
|
156
|
-
expect(Renamed.displayName).toBe(
|
|
157
|
-
expect(Original.displayName).toBe(
|
|
153
|
+
it('should update displayName when name is changed', () => {
|
|
154
|
+
const Original = attrs({ name: 'Original', component: BaseComponent })
|
|
155
|
+
const Renamed = Original.config({ name: 'Renamed' })
|
|
156
|
+
expect(Renamed.displayName).toBe('Renamed')
|
|
157
|
+
expect(Original.displayName).toBe('Original')
|
|
158
158
|
})
|
|
159
159
|
|
|
160
|
-
it(
|
|
160
|
+
it('should swap the rendered component', () => {
|
|
161
161
|
const AltComponent = (props: any) => ({
|
|
162
|
-
type:
|
|
163
|
-
props: { ...props,
|
|
162
|
+
type: 'span',
|
|
163
|
+
props: { ...props, 'data-testid': 'alt' },
|
|
164
164
|
children: props.label,
|
|
165
165
|
key: null,
|
|
166
166
|
})
|
|
167
167
|
|
|
168
|
-
const Original = attrs({ name:
|
|
168
|
+
const Original = attrs({ name: 'Test', component: BaseComponent })
|
|
169
169
|
const Swapped = Original.config({ component: AltComponent })
|
|
170
170
|
|
|
171
|
-
const result = Swapped({ label:
|
|
172
|
-
expect(result.props[
|
|
173
|
-
expect(result.children).toBe(
|
|
171
|
+
const result = Swapped({ label: 'swapped' }) as any
|
|
172
|
+
expect(result.props['data-testid']).toBe('alt')
|
|
173
|
+
expect(result.children).toBe('swapped')
|
|
174
174
|
})
|
|
175
175
|
|
|
176
|
-
it(
|
|
176
|
+
it('should preserve attrs chain after config swap', () => {
|
|
177
177
|
const AltComponent = (props: any) => ({
|
|
178
|
-
type:
|
|
179
|
-
props: { ...props,
|
|
178
|
+
type: 'span',
|
|
179
|
+
props: { ...props, 'data-testid': 'alt' },
|
|
180
180
|
children: props.label,
|
|
181
181
|
key: null,
|
|
182
182
|
})
|
|
183
183
|
|
|
184
|
-
const Component = attrs({ name:
|
|
185
|
-
.attrs(() => ({ label:
|
|
184
|
+
const Component = attrs({ name: 'Test', component: BaseComponent })
|
|
185
|
+
.attrs(() => ({ label: 'from-attrs' }))
|
|
186
186
|
.config({ component: AltComponent })
|
|
187
187
|
|
|
188
188
|
const result = Component({}) as any
|
|
189
|
-
expect(result.children).toBe(
|
|
189
|
+
expect(result.children).toBe('from-attrs')
|
|
190
190
|
})
|
|
191
191
|
})
|
|
192
192
|
|
|
193
193
|
// --------------------------------------------------------
|
|
194
194
|
// .statics() chaining
|
|
195
195
|
// --------------------------------------------------------
|
|
196
|
-
describe(
|
|
197
|
-
it(
|
|
196
|
+
describe('.statics() chaining', () => {
|
|
197
|
+
it('should assign statics to component meta', () => {
|
|
198
198
|
const Component = attrs({
|
|
199
|
-
name:
|
|
199
|
+
name: 'Test',
|
|
200
200
|
component: BaseComponent,
|
|
201
|
-
}).statics({ theme:
|
|
201
|
+
}).statics({ theme: 'dark', sizes: ['sm', 'md', 'lg'] })
|
|
202
202
|
|
|
203
203
|
expect(Component.meta).toEqual({
|
|
204
|
-
theme:
|
|
205
|
-
sizes: [
|
|
204
|
+
theme: 'dark',
|
|
205
|
+
sizes: ['sm', 'md', 'lg'],
|
|
206
206
|
})
|
|
207
207
|
})
|
|
208
208
|
|
|
209
|
-
it(
|
|
209
|
+
it('should merge statics across chains', () => {
|
|
210
210
|
const Component = attrs({
|
|
211
|
-
name:
|
|
211
|
+
name: 'Test',
|
|
212
212
|
component: BaseComponent,
|
|
213
213
|
})
|
|
214
|
-
.statics({ theme:
|
|
215
|
-
.statics({ variant:
|
|
214
|
+
.statics({ theme: 'dark' })
|
|
215
|
+
.statics({ variant: 'primary' })
|
|
216
216
|
|
|
217
217
|
expect(Component.meta).toEqual({
|
|
218
|
-
theme:
|
|
219
|
-
variant:
|
|
218
|
+
theme: 'dark',
|
|
219
|
+
variant: 'primary',
|
|
220
220
|
})
|
|
221
221
|
})
|
|
222
222
|
})
|
|
@@ -224,132 +224,132 @@ describe(".statics() chaining", () => {
|
|
|
224
224
|
// --------------------------------------------------------
|
|
225
225
|
// .compose() chaining
|
|
226
226
|
// --------------------------------------------------------
|
|
227
|
-
describe(
|
|
228
|
-
it(
|
|
227
|
+
describe('.compose() chaining', () => {
|
|
228
|
+
it('should wrap component with a HOC', () => {
|
|
229
229
|
const withWrapper = (WrappedComponent: any) => (props: any) => ({
|
|
230
|
-
type:
|
|
231
|
-
props: {
|
|
230
|
+
type: 'div',
|
|
231
|
+
props: { 'data-testid': 'hoc-wrapper' },
|
|
232
232
|
children: WrappedComponent(props),
|
|
233
233
|
key: null,
|
|
234
234
|
})
|
|
235
235
|
|
|
236
236
|
const Component = attrs({
|
|
237
|
-
name:
|
|
237
|
+
name: 'Test',
|
|
238
238
|
component: BaseComponent,
|
|
239
239
|
}).compose({ withWrapper })
|
|
240
240
|
|
|
241
|
-
const result = Component({ label:
|
|
242
|
-
expect(result.props[
|
|
243
|
-
expect(result.children.children).toBe(
|
|
241
|
+
const result = Component({ label: 'composed' }) as any
|
|
242
|
+
expect(result.props['data-testid']).toBe('hoc-wrapper')
|
|
243
|
+
expect(result.children.children).toBe('composed')
|
|
244
244
|
})
|
|
245
245
|
|
|
246
|
-
it(
|
|
246
|
+
it('should apply multiple HOCs in correct order', () => {
|
|
247
247
|
const order: string[] = []
|
|
248
248
|
|
|
249
249
|
const withOuter = (Wrapped: any) => (props: any) => {
|
|
250
|
-
order.push(
|
|
250
|
+
order.push('outer')
|
|
251
251
|
return Wrapped(props)
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
const withInner = (Wrapped: any) => (props: any) => {
|
|
255
|
-
order.push(
|
|
255
|
+
order.push('inner')
|
|
256
256
|
return Wrapped(props)
|
|
257
257
|
}
|
|
258
258
|
|
|
259
259
|
const Component = attrs({
|
|
260
|
-
name:
|
|
260
|
+
name: 'Test',
|
|
261
261
|
component: BaseComponent,
|
|
262
262
|
}).compose({ withOuter, withInner })
|
|
263
263
|
|
|
264
264
|
Component({})
|
|
265
265
|
// calculateHocsFuncs reverses the order: last-defined runs first
|
|
266
|
-
expect(order).toEqual([
|
|
266
|
+
expect(order).toEqual(['inner', 'outer'])
|
|
267
267
|
})
|
|
268
268
|
|
|
269
|
-
it(
|
|
269
|
+
it('should remove a HOC by setting it to false', () => {
|
|
270
270
|
const withWrapper = (WrappedComponent: any) => (props: any) => ({
|
|
271
|
-
type:
|
|
272
|
-
props: {
|
|
271
|
+
type: 'div',
|
|
272
|
+
props: { 'data-testid': 'hoc-wrapper' },
|
|
273
273
|
children: WrappedComponent(props),
|
|
274
274
|
key: null,
|
|
275
275
|
})
|
|
276
276
|
|
|
277
277
|
const WithHoc = attrs({
|
|
278
|
-
name:
|
|
278
|
+
name: 'Test',
|
|
279
279
|
component: BaseComponent,
|
|
280
280
|
}).compose({ withWrapper })
|
|
281
281
|
|
|
282
282
|
const WithoutHoc = WithHoc.compose({ withWrapper: false })
|
|
283
283
|
|
|
284
|
-
const result = WithoutHoc({ label:
|
|
284
|
+
const result = WithoutHoc({ label: 'no-hoc' }) as any
|
|
285
285
|
// Should render base component directly, no wrapper
|
|
286
|
-
expect(result.props[
|
|
287
|
-
expect(result.children).toBe(
|
|
286
|
+
expect(result.props['data-testid']).toBe('base')
|
|
287
|
+
expect(result.children).toBe('no-hoc')
|
|
288
288
|
})
|
|
289
289
|
})
|
|
290
290
|
|
|
291
291
|
// --------------------------------------------------------
|
|
292
292
|
// .getDefaultAttrs()
|
|
293
293
|
// --------------------------------------------------------
|
|
294
|
-
describe(
|
|
295
|
-
it(
|
|
294
|
+
describe('.getDefaultAttrs()', () => {
|
|
295
|
+
it('should return computed default attrs for given props', () => {
|
|
296
296
|
const Component = attrs({
|
|
297
|
-
name:
|
|
297
|
+
name: 'Test',
|
|
298
298
|
component: BaseComponent,
|
|
299
299
|
}).attrs((props: any) => ({
|
|
300
|
-
computed: props.variant ===
|
|
300
|
+
computed: props.variant === 'primary' ? 'blue' : 'gray',
|
|
301
301
|
}))
|
|
302
302
|
|
|
303
|
-
const defaults = Component.getDefaultAttrs({ variant:
|
|
304
|
-
expect(defaults).toEqual({ computed:
|
|
303
|
+
const defaults = Component.getDefaultAttrs({ variant: 'primary' })
|
|
304
|
+
expect(defaults).toEqual({ computed: 'blue' })
|
|
305
305
|
})
|
|
306
306
|
|
|
307
|
-
it(
|
|
308
|
-
const Component = attrs({ name:
|
|
307
|
+
it('should return empty object when no attrs defined', () => {
|
|
308
|
+
const Component = attrs({ name: 'Test', component: BaseComponent })
|
|
309
309
|
const defaults = Component.getDefaultAttrs({})
|
|
310
310
|
expect(defaults).toEqual({})
|
|
311
311
|
})
|
|
312
312
|
|
|
313
|
-
it(
|
|
313
|
+
it('should merge multiple attrs chains', () => {
|
|
314
314
|
const Component = attrs({
|
|
315
|
-
name:
|
|
315
|
+
name: 'Test',
|
|
316
316
|
component: BaseComponent,
|
|
317
317
|
})
|
|
318
|
-
.attrs(() => ({ color:
|
|
319
|
-
.attrs(() => ({ size:
|
|
318
|
+
.attrs(() => ({ color: 'blue' }))
|
|
319
|
+
.attrs(() => ({ size: 'lg' }))
|
|
320
320
|
|
|
321
321
|
const defaults = Component.getDefaultAttrs({})
|
|
322
|
-
expect(defaults).toEqual({ color:
|
|
322
|
+
expect(defaults).toEqual({ color: 'blue', size: 'lg' })
|
|
323
323
|
})
|
|
324
324
|
})
|
|
325
325
|
|
|
326
326
|
// --------------------------------------------------------
|
|
327
327
|
// isAttrsComponent
|
|
328
328
|
// --------------------------------------------------------
|
|
329
|
-
describe(
|
|
330
|
-
it(
|
|
331
|
-
const Component = attrs({ name:
|
|
329
|
+
describe('isAttrsComponent', () => {
|
|
330
|
+
it('should return true for attrs components', () => {
|
|
331
|
+
const Component = attrs({ name: 'Test', component: BaseComponent })
|
|
332
332
|
expect(isAttrsComponent(Component)).toBe(true)
|
|
333
333
|
})
|
|
334
334
|
|
|
335
|
-
it(
|
|
335
|
+
it('should return false for plain components', () => {
|
|
336
336
|
expect(isAttrsComponent(BaseComponent)).toBe(false)
|
|
337
337
|
})
|
|
338
338
|
|
|
339
|
-
it(
|
|
339
|
+
it('should return false for null', () => {
|
|
340
340
|
expect(isAttrsComponent(null)).toBe(false)
|
|
341
341
|
})
|
|
342
342
|
|
|
343
|
-
it(
|
|
343
|
+
it('should return false for undefined', () => {
|
|
344
344
|
expect(isAttrsComponent(undefined)).toBe(false)
|
|
345
345
|
})
|
|
346
346
|
|
|
347
|
-
it(
|
|
348
|
-
expect(isAttrsComponent(
|
|
347
|
+
it('should return false for non-objects', () => {
|
|
348
|
+
expect(isAttrsComponent('string')).toBe(false)
|
|
349
349
|
expect(isAttrsComponent(123)).toBe(false)
|
|
350
350
|
})
|
|
351
351
|
|
|
352
|
-
it(
|
|
352
|
+
it('should return true for objects with IS_ATTRS property', () => {
|
|
353
353
|
expect(isAttrsComponent({ IS_ATTRS: true })).toBe(true)
|
|
354
354
|
})
|
|
355
355
|
})
|
|
@@ -357,15 +357,15 @@ describe("isAttrsComponent", () => {
|
|
|
357
357
|
// --------------------------------------------------------
|
|
358
358
|
// displayName fallback
|
|
359
359
|
// --------------------------------------------------------
|
|
360
|
-
describe(
|
|
361
|
-
it(
|
|
360
|
+
describe('displayName resolution', () => {
|
|
361
|
+
it('should fall back to component.displayName when name is not provided', () => {
|
|
362
362
|
const NamedComponent = (props: any) => ({
|
|
363
|
-
type:
|
|
363
|
+
type: 'div',
|
|
364
364
|
props,
|
|
365
365
|
children: props.children,
|
|
366
366
|
key: null,
|
|
367
367
|
})
|
|
368
|
-
NamedComponent.displayName =
|
|
368
|
+
NamedComponent.displayName = 'MyDisplayName'
|
|
369
369
|
|
|
370
370
|
const Component = attrsComponent({
|
|
371
371
|
name: undefined as any,
|
|
@@ -376,13 +376,13 @@ describe("displayName resolution", () => {
|
|
|
376
376
|
compose: {},
|
|
377
377
|
statics: {},
|
|
378
378
|
})
|
|
379
|
-
expect(Component.displayName).toBe(
|
|
379
|
+
expect(Component.displayName).toBe('MyDisplayName')
|
|
380
380
|
})
|
|
381
381
|
|
|
382
|
-
it(
|
|
382
|
+
it('should fall back to component.name when name and displayName are not provided', () => {
|
|
383
383
|
function ExplicitNameComponent(props: any) {
|
|
384
384
|
return {
|
|
385
|
-
type:
|
|
385
|
+
type: 'div',
|
|
386
386
|
props,
|
|
387
387
|
children: props.children,
|
|
388
388
|
key: null,
|
|
@@ -398,16 +398,16 @@ describe("displayName resolution", () => {
|
|
|
398
398
|
compose: {},
|
|
399
399
|
statics: {},
|
|
400
400
|
})
|
|
401
|
-
expect(Component.displayName).toBe(
|
|
401
|
+
expect(Component.displayName).toBe('ExplicitNameComponent')
|
|
402
402
|
})
|
|
403
403
|
})
|
|
404
404
|
|
|
405
405
|
// --------------------------------------------------------
|
|
406
406
|
// Ref as normal prop
|
|
407
407
|
// --------------------------------------------------------
|
|
408
|
-
describe(
|
|
409
|
-
it(
|
|
410
|
-
const Component = attrs({ name:
|
|
408
|
+
describe('ref passthrough', () => {
|
|
409
|
+
it('should pass ref as a normal prop through the chain', () => {
|
|
410
|
+
const Component = attrs({ name: 'Test', component: BaseComponent })
|
|
411
411
|
const refObj = { current: null }
|
|
412
412
|
|
|
413
413
|
const result = renderProps(Component, { ref: refObj })
|
|
@@ -418,10 +418,10 @@ describe("ref passthrough", () => {
|
|
|
418
418
|
// --------------------------------------------------------
|
|
419
419
|
// Immutability
|
|
420
420
|
// --------------------------------------------------------
|
|
421
|
-
describe(
|
|
422
|
-
it(
|
|
423
|
-
const Base = attrs({ name:
|
|
424
|
-
const WithAttrs = Base.attrs(() => ({ label:
|
|
421
|
+
describe('immutability', () => {
|
|
422
|
+
it('should return new instances on each chain call', () => {
|
|
423
|
+
const Base = attrs({ name: 'Test', component: BaseComponent })
|
|
424
|
+
const WithAttrs = Base.attrs(() => ({ label: 'a' }))
|
|
425
425
|
const WithStatics = Base.statics({ x: 1 })
|
|
426
426
|
|
|
427
427
|
expect(Base).not.toBe(WithAttrs)
|
|
@@ -429,60 +429,60 @@ describe("immutability", () => {
|
|
|
429
429
|
expect(WithAttrs).not.toBe(WithStatics)
|
|
430
430
|
})
|
|
431
431
|
|
|
432
|
-
it(
|
|
432
|
+
it('should not affect parent when child is modified', () => {
|
|
433
433
|
const Parent = attrs({
|
|
434
|
-
name:
|
|
434
|
+
name: 'Parent',
|
|
435
435
|
component: BaseComponent,
|
|
436
|
-
}).attrs(() => ({ label:
|
|
436
|
+
}).attrs(() => ({ label: 'Parent' }))
|
|
437
437
|
|
|
438
|
-
const Child = Parent.attrs(() => ({ label:
|
|
438
|
+
const Child = Parent.attrs(() => ({ label: 'Child' }))
|
|
439
439
|
|
|
440
440
|
const parentResult = renderProps(Parent)
|
|
441
|
-
expect(parentResult.label).toBe(
|
|
441
|
+
expect(parentResult.label).toBe('Parent')
|
|
442
442
|
|
|
443
443
|
const childResult = renderProps(Child)
|
|
444
|
-
expect(childResult.label).toBe(
|
|
444
|
+
expect(childResult.label).toBe('Child')
|
|
445
445
|
})
|
|
446
446
|
})
|
|
447
447
|
|
|
448
448
|
// --------------------------------------------------------
|
|
449
449
|
// Deep chaining
|
|
450
450
|
// --------------------------------------------------------
|
|
451
|
-
describe(
|
|
452
|
-
it(
|
|
453
|
-
const Component = attrs({ name:
|
|
454
|
-
.attrs(() => ({
|
|
455
|
-
.attrs(() => ({
|
|
456
|
-
.attrs(() => ({
|
|
451
|
+
describe('deep chaining', () => {
|
|
452
|
+
it('should accumulate attrs across 3+ levels', () => {
|
|
453
|
+
const Component = attrs({ name: 'Test', component: BaseComponent })
|
|
454
|
+
.attrs(() => ({ 'data-a': '1' }))
|
|
455
|
+
.attrs(() => ({ 'data-b': '2' }))
|
|
456
|
+
.attrs(() => ({ 'data-c': '3' }))
|
|
457
457
|
|
|
458
458
|
const result = renderProps(Component)
|
|
459
|
-
expect(result[
|
|
460
|
-
expect(result[
|
|
461
|
-
expect(result[
|
|
459
|
+
expect(result['data-a']).toBe('1')
|
|
460
|
+
expect(result['data-b']).toBe('2')
|
|
461
|
+
expect(result['data-c']).toBe('3')
|
|
462
462
|
})
|
|
463
463
|
|
|
464
|
-
it(
|
|
465
|
-
const Component = attrs({ name:
|
|
466
|
-
.attrs(() => ({ label:
|
|
467
|
-
.statics({ variant:
|
|
468
|
-
.config({ name:
|
|
469
|
-
.attrs(() => ({
|
|
464
|
+
it('should combine attrs, statics, and config in a single chain', () => {
|
|
465
|
+
const Component = attrs({ name: 'Base', component: BaseComponent })
|
|
466
|
+
.attrs(() => ({ label: 'hello' }))
|
|
467
|
+
.statics({ variant: 'primary' })
|
|
468
|
+
.config({ name: 'FinalName' })
|
|
469
|
+
.attrs(() => ({ 'data-extra': 'yes' }))
|
|
470
470
|
|
|
471
|
-
expect(Component.displayName).toBe(
|
|
472
|
-
expect(Component.meta).toEqual({ variant:
|
|
471
|
+
expect(Component.displayName).toBe('FinalName')
|
|
472
|
+
expect(Component.meta).toEqual({ variant: 'primary' })
|
|
473
473
|
|
|
474
474
|
const result = renderProps(Component)
|
|
475
|
-
expect(result.label).toBe(
|
|
476
|
-
expect(result[
|
|
475
|
+
expect(result.label).toBe('hello')
|
|
476
|
+
expect(result['data-extra']).toBe('yes')
|
|
477
477
|
})
|
|
478
478
|
|
|
479
|
-
it(
|
|
480
|
-
const Component = attrs({ name:
|
|
481
|
-
.attrs(() => ({ label:
|
|
482
|
-
.attrs(() => ({ label:
|
|
483
|
-
.attrs(() => ({ label:
|
|
479
|
+
it('should allow later attrs to override earlier ones', () => {
|
|
480
|
+
const Component = attrs({ name: 'Test', component: BaseComponent })
|
|
481
|
+
.attrs(() => ({ label: 'first' }))
|
|
482
|
+
.attrs(() => ({ label: 'second' }))
|
|
483
|
+
.attrs(() => ({ label: 'third' }))
|
|
484
484
|
|
|
485
485
|
const result = renderProps(Component)
|
|
486
|
-
expect(result.label).toBe(
|
|
486
|
+
expect(result.label).toBe('third')
|
|
487
487
|
})
|
|
488
488
|
})
|