@lifeonlars/prime-yggdrasil 0.2.0 → 0.2.2
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/dist/components/button.css +16 -0
- package/dist/components/form.css +150 -33
- package/dist/index.cjs +6 -1
- package/dist/index.js +280 -5
- package/dist/typography.css +73 -0
- package/dist/yggdrasil-dark.css +1 -1
- package/dist/yggdrasil-light.css +1 -1
- package/docs/CONSUMPTION-GUIDE.md +60 -2
- package/docs/STORYBOOK-TESTING.md +352 -0
- package/package.json +16 -8
|
@@ -388,6 +388,22 @@
|
|
|
388
388
|
.p-splitbutton {
|
|
389
389
|
border-radius: var(--radius-md);
|
|
390
390
|
}
|
|
391
|
+
|
|
392
|
+
/* SplitButton border radius overrides for grouped buttons */
|
|
393
|
+
.p-splitbutton > .p-button {
|
|
394
|
+
border-radius: 0;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.p-splitbutton > .p-button:first-child {
|
|
398
|
+
border-top-left-radius: var(--radius-md);
|
|
399
|
+
border-bottom-left-radius: var(--radius-md);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.p-splitbutton > .p-button:last-child {
|
|
403
|
+
border-top-right-radius: var(--radius-md);
|
|
404
|
+
border-bottom-right-radius: var(--radius-md);
|
|
405
|
+
}
|
|
406
|
+
|
|
391
407
|
.p-splitbutton.p-button-outlined > .p-button {
|
|
392
408
|
background-color: transparent;
|
|
393
409
|
color: var(--text-state-interactive);
|
package/dist/components/form.css
CHANGED
|
@@ -195,16 +195,30 @@
|
|
|
195
195
|
display: inline-flex;
|
|
196
196
|
user-select: none;
|
|
197
197
|
vertical-align: bottom;
|
|
198
|
-
}
|
|
199
|
-
.p-checkbox {
|
|
200
198
|
width: 22px;
|
|
201
199
|
height: 22px;
|
|
202
200
|
}
|
|
201
|
+
|
|
203
202
|
.p-checkbox .p-checkbox-input {
|
|
203
|
+
appearance: none;
|
|
204
|
+
position: absolute;
|
|
205
|
+
top: 0;
|
|
206
|
+
left: 0;
|
|
207
|
+
width: 100%;
|
|
208
|
+
height: 100%;
|
|
209
|
+
padding: 0;
|
|
210
|
+
margin: 0;
|
|
211
|
+
opacity: 0;
|
|
212
|
+
z-index: 1;
|
|
213
|
+
outline: 0 none;
|
|
204
214
|
border: 2px solid var(--border-neutral-default);
|
|
205
215
|
border-radius: var(--radius-md);
|
|
206
216
|
}
|
|
217
|
+
|
|
207
218
|
.p-checkbox .p-checkbox-box {
|
|
219
|
+
display: flex;
|
|
220
|
+
justify-content: center;
|
|
221
|
+
align-items: center;
|
|
208
222
|
border: 2px solid var(--border-neutral-default);
|
|
209
223
|
background: var(--surface-neutral-primary);
|
|
210
224
|
width: 22px;
|
|
@@ -214,30 +228,13 @@
|
|
|
214
228
|
transition: background-color 0.2s, color 0.2s, border-color 0.2s, box-shadow 0.2s;
|
|
215
229
|
outline-color: transparent;
|
|
216
230
|
}
|
|
231
|
+
|
|
217
232
|
.p-checkbox .p-checkbox-box .p-checkbox-icon {
|
|
218
233
|
transition-duration: 0.2s;
|
|
219
234
|
color: var(--text-onsurface-onbrand);
|
|
220
235
|
font-size: 14px;
|
|
221
236
|
}
|
|
222
|
-
|
|
223
|
-
width: 14px;
|
|
224
|
-
height: 14px;
|
|
225
|
-
}
|
|
226
|
-
.p-checkbox .p-checkbox-box {
|
|
227
|
-
border: 2px solid var(--border-neutral-default);
|
|
228
|
-
background: var(--surface-neutral-primary);
|
|
229
|
-
width: 22px;
|
|
230
|
-
height: 22px;
|
|
231
|
-
color: var(--text-neutral-default);
|
|
232
|
-
border-radius: var(--radius-md);
|
|
233
|
-
transition: background-color 0.2s, color 0.2s, border-color 0.2s, box-shadow 0.2s;
|
|
234
|
-
outline-color: transparent;
|
|
235
|
-
}
|
|
236
|
-
.p-checkbox .p-checkbox-box .p-checkbox-icon {
|
|
237
|
-
transition-duration: 0.2s;
|
|
238
|
-
color: var(--text-onsurface-onbrand);
|
|
239
|
-
font-size: 14px;
|
|
240
|
-
}
|
|
237
|
+
|
|
241
238
|
.p-checkbox .p-checkbox-box .p-checkbox-icon.p-icon {
|
|
242
239
|
width: 14px;
|
|
243
240
|
height: 14px;
|
|
@@ -275,23 +272,13 @@
|
|
|
275
272
|
.p-checkbox.p-variant-filled:not(.p-disabled):has(.p-checkbox-input:hover).p-highlight .p-checkbox-box {
|
|
276
273
|
background: var(--surface-brand-secondary);
|
|
277
274
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
display: inline-flex;
|
|
281
|
-
user-select: none;
|
|
282
|
-
vertical-align: bottom;
|
|
283
|
-
}
|
|
275
|
+
|
|
276
|
+
/* Radio Button */
|
|
284
277
|
.p-radiobutton {
|
|
285
278
|
position: relative;
|
|
286
279
|
display: inline-flex;
|
|
287
280
|
user-select: none;
|
|
288
281
|
vertical-align: bottom;
|
|
289
|
-
}
|
|
290
|
-
.p-radiobutton.p-highlight .p-radiobutton-icon {
|
|
291
|
-
transform: translateZ(0) scale(1, 1);
|
|
292
|
-
visibility: visible;
|
|
293
|
-
}
|
|
294
|
-
.p-radiobutton {
|
|
295
282
|
width: 22px;
|
|
296
283
|
height: 22px;
|
|
297
284
|
}
|
|
@@ -311,6 +298,9 @@
|
|
|
311
298
|
border-radius: var(--radius-full);
|
|
312
299
|
}
|
|
313
300
|
.p-radiobutton .p-radiobutton-box {
|
|
301
|
+
display: flex;
|
|
302
|
+
justify-content: center;
|
|
303
|
+
align-items: center;
|
|
314
304
|
border: 2px solid var(--border-neutral-default);
|
|
315
305
|
background: var(--surface-neutral-primary);
|
|
316
306
|
width: 22px;
|
|
@@ -323,6 +313,7 @@
|
|
|
323
313
|
.p-radiobutton .p-radiobutton-box .p-radiobutton-icon {
|
|
324
314
|
width: 12px;
|
|
325
315
|
height: 12px;
|
|
316
|
+
border-radius: 50%;
|
|
326
317
|
transition-duration: 0.2s;
|
|
327
318
|
background-color: var(--surface-neutral-primary);
|
|
328
319
|
}
|
|
@@ -475,6 +466,22 @@
|
|
|
475
466
|
.p-selectbutton.p-invalid > .p-button {
|
|
476
467
|
border-color: var(--severity-danger-text);
|
|
477
468
|
}
|
|
469
|
+
|
|
470
|
+
/* SelectButton border radius overrides for grouped buttons */
|
|
471
|
+
.p-selectbutton .p-button {
|
|
472
|
+
border-radius: 0;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.p-selectbutton .p-button:first-child {
|
|
476
|
+
border-top-left-radius: var(--radius-md);
|
|
477
|
+
border-bottom-left-radius: var(--radius-md);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
.p-selectbutton .p-button:last-child {
|
|
481
|
+
border-top-right-radius: var(--radius-md);
|
|
482
|
+
border-bottom-right-radius: var(--radius-md);
|
|
483
|
+
}
|
|
484
|
+
|
|
478
485
|
.p-selectbutton > .p-button,
|
|
479
486
|
.p-dropdown {
|
|
480
487
|
display: inline-flex;
|
|
@@ -747,6 +754,116 @@
|
|
|
747
754
|
.p-multiselect.p-invalid.p-component {
|
|
748
755
|
border-color: var(--severity-danger-text);
|
|
749
756
|
}
|
|
757
|
+
|
|
758
|
+
/* MultiSelect Panel */
|
|
759
|
+
.p-multiselect-panel {
|
|
760
|
+
background: var(--surface-neutral-primary);
|
|
761
|
+
color: var(--text-neutral-default);
|
|
762
|
+
border: 0 none;
|
|
763
|
+
border-radius: var(--radius-md);
|
|
764
|
+
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
.p-multiselect-panel .p-multiselect-header {
|
|
768
|
+
padding: 0.75rem 1.25rem;
|
|
769
|
+
border-bottom: 1px solid var(--border-neutral-subdued);
|
|
770
|
+
color: var(--text-neutral-loud);
|
|
771
|
+
background: var(--surface-neutral-secondary);
|
|
772
|
+
margin: 0;
|
|
773
|
+
border-top-right-radius: var(--radius-md);
|
|
774
|
+
border-top-left-radius: var(--radius-md);
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
.p-multiselect-panel .p-multiselect-header .p-multiselect-filter-container .p-inputtext {
|
|
778
|
+
padding-right: 1.75rem;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
.p-multiselect-panel .p-multiselect-header .p-multiselect-filter-container .p-multiselect-filter-icon {
|
|
782
|
+
right: 0.75rem;
|
|
783
|
+
color: var(--text-neutral-subdued);
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
.p-multiselect-panel .p-multiselect-header .p-checkbox {
|
|
787
|
+
margin-right: 0.5rem;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
.p-multiselect-panel .p-multiselect-header .p-multiselect-close {
|
|
791
|
+
margin-left: 0.5rem;
|
|
792
|
+
width: 2rem;
|
|
793
|
+
height: 2rem;
|
|
794
|
+
color: var(--text-neutral-subdued);
|
|
795
|
+
border: 0 none;
|
|
796
|
+
background: transparent;
|
|
797
|
+
border-radius: var(--radius-full);
|
|
798
|
+
transition: background-color 0.2s, color 0.2s, box-shadow 0.2s;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
.p-multiselect-panel .p-multiselect-header .p-multiselect-close:enabled:hover {
|
|
802
|
+
color: var(--text-neutral-loud);
|
|
803
|
+
border-color: transparent;
|
|
804
|
+
background: var(--surface-state-hover);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
.p-multiselect-panel .p-multiselect-header .p-multiselect-close:focus-visible {
|
|
808
|
+
outline: 0 none;
|
|
809
|
+
outline-offset: 0;
|
|
810
|
+
box-shadow: 0 0 0 0.2rem var(--surface-brand-overlay);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
.p-multiselect-panel .p-multiselect-items {
|
|
814
|
+
padding: 0.75rem 0;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
.p-multiselect-panel .p-multiselect-items .p-multiselect-item {
|
|
818
|
+
margin: 0;
|
|
819
|
+
padding: 0.75rem 1.25rem;
|
|
820
|
+
border: 0 none;
|
|
821
|
+
color: var(--text-neutral-default);
|
|
822
|
+
background: transparent;
|
|
823
|
+
transition: box-shadow 0.2s;
|
|
824
|
+
border-radius: 0;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
.p-multiselect-panel .p-multiselect-items .p-multiselect-item:first-child {
|
|
828
|
+
margin-top: 0;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
.p-multiselect-panel .p-multiselect-items .p-multiselect-item:last-child {
|
|
832
|
+
margin-bottom: 0;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
.p-multiselect-panel .p-multiselect-items .p-multiselect-item.p-highlight {
|
|
836
|
+
color: var(--text-state-interactive);
|
|
837
|
+
background: var(--surface-state-selected);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
.p-multiselect-panel .p-multiselect-items .p-multiselect-item.p-highlight.p-focus {
|
|
841
|
+
background: color-mix(in srgb, var(--surface-brand-primary) 24%, transparent);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
.p-multiselect-panel .p-multiselect-items .p-multiselect-item:not(.p-highlight, .p-disabled).p-focus {
|
|
845
|
+
color: var(--text-neutral-default);
|
|
846
|
+
background: var(--surface-state-hover);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
.p-multiselect-panel .p-multiselect-items .p-multiselect-item .p-checkbox {
|
|
850
|
+
margin-right: 0.5rem;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
.p-multiselect-panel .p-multiselect-items .p-multiselect-item-group {
|
|
854
|
+
margin: 0;
|
|
855
|
+
padding: 0.75rem 1.25rem;
|
|
856
|
+
color: var(--text-neutral-loud);
|
|
857
|
+
background: var(--surface-neutral-primary);
|
|
858
|
+
font-weight: 700;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
.p-multiselect-panel .p-multiselect-items .p-multiselect-empty-message {
|
|
862
|
+
padding: 0.75rem 1.25rem;
|
|
863
|
+
color: var(--text-neutral-default);
|
|
864
|
+
background: transparent;
|
|
865
|
+
}
|
|
866
|
+
|
|
750
867
|
.p-treeselect {
|
|
751
868
|
background: var(--text-onsurface-onbrand);
|
|
752
869
|
border: 1px solid var(--border-neutral-subdued);
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const K=require("react"),ee=require("primereact/api");var b={exports:{}},_={};var $;function re(){if($)return _;$=1;var u=Symbol.for("react.transitional.element"),i=Symbol.for("react.fragment");function l(c,o,s){var d=null;if(s!==void 0&&(d=""+s),o.key!==void 0&&(d=""+o.key),"key"in o){s={};for(var m in o)m!=="key"&&(s[m]=o[m])}else s=o;return o=s.ref,{$$typeof:u,type:c,key:d,ref:o!==void 0?o:null,props:s}}return _.Fragment=i,_.jsx=l,_.jsxs=l,_}var E={};var I;function te(){return I||(I=1,process.env.NODE_ENV!=="production"&&(function(){function u(e){if(e==null)return null;if(typeof e=="function")return e.$$typeof===H?null:e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case p:return"Fragment";case q:return"Profiler";case W:return"StrictMode";case z:return"Suspense";case G:return"SuspenseList";case B:return"Activity"}if(typeof e=="object")switch(typeof e.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),e.$$typeof){case L:return"Portal";case J:return e.displayName||"Context";case U:return(e._context.displayName||"Context")+".Consumer";case V:var r=e.render;return e=e.displayName,e||(e=r.displayName||r.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case X:return r=e.displayName||null,r!==null?r:u(e.type)||"Memo";case T:r=e._payload,e=e._init;try{return u(e(r))}catch{}}return null}function i(e){return""+e}function l(e){try{i(e);var r=!1}catch{r=!0}if(r){r=console;var t=r.error,n=typeof Symbol=="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return t.call(r,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",n),i(e)}}function c(e){if(e===p)return"<>";if(typeof e=="object"&&e!==null&&e.$$typeof===T)return"<...>";try{var r=u(e);return r?"<"+r+">":"<...>"}catch{return"<...>"}}function o(){var e=k.A;return e===null?null:e.getOwner()}function s(){return Error("react-stack-top-frame")}function d(e){if(w.call(e,"key")){var r=Object.getOwnPropertyDescriptor(e,"key").get;if(r&&r.isReactWarning)return!1}return e.key!==void 0}function m(e,r){function t(){h||(h=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",r))}t.isReactWarning=!0,Object.defineProperty(e,"key",{get:t,configurable:!0})}function D(){var e=u(this.type);return x[e]||(x[e]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),e=this.props.ref,e!==void 0?e:null}function M(e,r,t,n,R,O){var a=t.ref;return e={$$typeof:j,type:e,key:r,props:t,_owner:n},(a!==void 0?a:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:D}):Object.defineProperty(e,"ref",{enumerable:!1,value:null}),e._store={},Object.defineProperty(e._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:R}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:O}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function S(e,r,t,n,R,O){var a=r.children;if(a!==void 0)if(n)if(Z(a)){for(n=0;n<a.length;n++)A(a[n]);Object.freeze&&Object.freeze(a)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else A(a);if(w.call(r,"key")){a=u(e);var f=Object.keys(r).filter(function(Q){return Q!=="key"});n=0<f.length?"{key: someKey, "+f.join(": ..., ")+": ...}":"{key: someKey}",Y[a+n]||(f=0<f.length?"{"+f.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX:
|
|
2
|
+
let props = %s;
|
|
3
|
+
<%s {...props} />
|
|
4
|
+
React keys must be passed directly to JSX without using spread:
|
|
5
|
+
let props = %s;
|
|
6
|
+
<%s key={someKey} {...props} />`,n,a,f,a),Y[a+n]=!0)}if(a=null,t!==void 0&&(l(t),a=""+t),d(r)&&(l(r.key),a=""+r.key),"key"in r){t={};for(var P in r)P!=="key"&&(t[P]=r[P])}else t=r;return a&&m(t,typeof e=="function"?e.displayName||e.name||"Unknown":e),M(e,a,t,o(),R,O)}function A(e){y(e)?e._store&&(e._store.validated=1):typeof e=="object"&&e!==null&&e.$$typeof===T&&(e._payload.status==="fulfilled"?y(e._payload.value)&&e._payload.value._store&&(e._payload.value._store.validated=1):e._store&&(e._store.validated=1))}function y(e){return typeof e=="object"&&e!==null&&e.$$typeof===j}var v=K,j=Symbol.for("react.transitional.element"),L=Symbol.for("react.portal"),p=Symbol.for("react.fragment"),W=Symbol.for("react.strict_mode"),q=Symbol.for("react.profiler"),U=Symbol.for("react.consumer"),J=Symbol.for("react.context"),V=Symbol.for("react.forward_ref"),z=Symbol.for("react.suspense"),G=Symbol.for("react.suspense_list"),X=Symbol.for("react.memo"),T=Symbol.for("react.lazy"),B=Symbol.for("react.activity"),H=Symbol.for("react.client.reference"),k=v.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,w=Object.prototype.hasOwnProperty,Z=Array.isArray,g=console.createTask?console.createTask:function(){return null};v={react_stack_bottom_frame:function(e){return e()}};var h,x={},N=v.react_stack_bottom_frame.bind(v,s)(),C=g(c(s)),Y={};E.Fragment=p,E.jsx=function(e,r,t){var n=1e4>k.recentlyCreatedOwnerStacks++;return S(e,r,t,!1,n?Error("react-stack-top-frame"):N,n?g(c(e)):C)},E.jsxs=function(e,r,t){var n=1e4>k.recentlyCreatedOwnerStacks++;return S(e,r,t,!0,n?Error("react-stack-top-frame"):N,n?g(c(e)):C)}})()),E}var F;function ne(){return F||(F=1,process.env.NODE_ENV==="production"?b.exports=re():b.exports=te()),b.exports}var ae=ne();function oe({children:u,ripple:i=!0,primeConfig:l={}}){const c={ripple:i,inputStyle:"outlined",...l};return ae.jsx(ee.PrimeReactProvider,{value:c,children:u})}const se="0.2.1",ue={name:"Yggdrasil",version:"0.2.1",tokens:727,coverage:"100%",modes:["light","dark"]};exports.YggdrasilProvider=oe;exports.theme=ue;exports.version=se;
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,286 @@
|
|
|
1
|
-
|
|
1
|
+
import K from "react";
|
|
2
|
+
import { PrimeReactProvider as ee } from "primereact/api";
|
|
3
|
+
var b = { exports: {} }, _ = {};
|
|
4
|
+
var $;
|
|
5
|
+
function re() {
|
|
6
|
+
if ($) return _;
|
|
7
|
+
$ = 1;
|
|
8
|
+
var u = /* @__PURE__ */ Symbol.for("react.transitional.element"), i = /* @__PURE__ */ Symbol.for("react.fragment");
|
|
9
|
+
function l(c, o, s) {
|
|
10
|
+
var d = null;
|
|
11
|
+
if (s !== void 0 && (d = "" + s), o.key !== void 0 && (d = "" + o.key), "key" in o) {
|
|
12
|
+
s = {};
|
|
13
|
+
for (var m in o)
|
|
14
|
+
m !== "key" && (s[m] = o[m]);
|
|
15
|
+
} else s = o;
|
|
16
|
+
return o = s.ref, {
|
|
17
|
+
$$typeof: u,
|
|
18
|
+
type: c,
|
|
19
|
+
key: d,
|
|
20
|
+
ref: o !== void 0 ? o : null,
|
|
21
|
+
props: s
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return _.Fragment = i, _.jsx = l, _.jsxs = l, _;
|
|
25
|
+
}
|
|
26
|
+
var E = {};
|
|
27
|
+
var I;
|
|
28
|
+
function te() {
|
|
29
|
+
return I || (I = 1, process.env.NODE_ENV !== "production" && (function() {
|
|
30
|
+
function u(e) {
|
|
31
|
+
if (e == null) return null;
|
|
32
|
+
if (typeof e == "function")
|
|
33
|
+
return e.$$typeof === H ? null : e.displayName || e.name || null;
|
|
34
|
+
if (typeof e == "string") return e;
|
|
35
|
+
switch (e) {
|
|
36
|
+
case p:
|
|
37
|
+
return "Fragment";
|
|
38
|
+
case U:
|
|
39
|
+
return "Profiler";
|
|
40
|
+
case W:
|
|
41
|
+
return "StrictMode";
|
|
42
|
+
case z:
|
|
43
|
+
return "Suspense";
|
|
44
|
+
case G:
|
|
45
|
+
return "SuspenseList";
|
|
46
|
+
case B:
|
|
47
|
+
return "Activity";
|
|
48
|
+
}
|
|
49
|
+
if (typeof e == "object")
|
|
50
|
+
switch (typeof e.tag == "number" && console.error(
|
|
51
|
+
"Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."
|
|
52
|
+
), e.$$typeof) {
|
|
53
|
+
case M:
|
|
54
|
+
return "Portal";
|
|
55
|
+
case J:
|
|
56
|
+
return e.displayName || "Context";
|
|
57
|
+
case q:
|
|
58
|
+
return (e._context.displayName || "Context") + ".Consumer";
|
|
59
|
+
case V:
|
|
60
|
+
var r = e.render;
|
|
61
|
+
return e = e.displayName, e || (e = r.displayName || r.name || "", e = e !== "" ? "ForwardRef(" + e + ")" : "ForwardRef"), e;
|
|
62
|
+
case X:
|
|
63
|
+
return r = e.displayName || null, r !== null ? r : u(e.type) || "Memo";
|
|
64
|
+
case T:
|
|
65
|
+
r = e._payload, e = e._init;
|
|
66
|
+
try {
|
|
67
|
+
return u(e(r));
|
|
68
|
+
} catch {
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
function i(e) {
|
|
74
|
+
return "" + e;
|
|
75
|
+
}
|
|
76
|
+
function l(e) {
|
|
77
|
+
try {
|
|
78
|
+
i(e);
|
|
79
|
+
var r = !1;
|
|
80
|
+
} catch {
|
|
81
|
+
r = !0;
|
|
82
|
+
}
|
|
83
|
+
if (r) {
|
|
84
|
+
r = console;
|
|
85
|
+
var t = r.error, n = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object";
|
|
86
|
+
return t.call(
|
|
87
|
+
r,
|
|
88
|
+
"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",
|
|
89
|
+
n
|
|
90
|
+
), i(e);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function c(e) {
|
|
94
|
+
if (e === p) return "<>";
|
|
95
|
+
if (typeof e == "object" && e !== null && e.$$typeof === T)
|
|
96
|
+
return "<...>";
|
|
97
|
+
try {
|
|
98
|
+
var r = u(e);
|
|
99
|
+
return r ? "<" + r + ">" : "<...>";
|
|
100
|
+
} catch {
|
|
101
|
+
return "<...>";
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function o() {
|
|
105
|
+
var e = k.A;
|
|
106
|
+
return e === null ? null : e.getOwner();
|
|
107
|
+
}
|
|
108
|
+
function s() {
|
|
109
|
+
return Error("react-stack-top-frame");
|
|
110
|
+
}
|
|
111
|
+
function d(e) {
|
|
112
|
+
if (x.call(e, "key")) {
|
|
113
|
+
var r = Object.getOwnPropertyDescriptor(e, "key").get;
|
|
114
|
+
if (r && r.isReactWarning) return !1;
|
|
115
|
+
}
|
|
116
|
+
return e.key !== void 0;
|
|
117
|
+
}
|
|
118
|
+
function m(e, r) {
|
|
119
|
+
function t() {
|
|
120
|
+
j || (j = !0, console.error(
|
|
121
|
+
"%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",
|
|
122
|
+
r
|
|
123
|
+
));
|
|
124
|
+
}
|
|
125
|
+
t.isReactWarning = !0, Object.defineProperty(e, "key", {
|
|
126
|
+
get: t,
|
|
127
|
+
configurable: !0
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
function D() {
|
|
131
|
+
var e = u(this.type);
|
|
132
|
+
return h[e] || (h[e] = !0, console.error(
|
|
133
|
+
"Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."
|
|
134
|
+
)), e = this.props.ref, e !== void 0 ? e : null;
|
|
135
|
+
}
|
|
136
|
+
function L(e, r, t, n, R, P) {
|
|
137
|
+
var a = t.ref;
|
|
138
|
+
return e = {
|
|
139
|
+
$$typeof: w,
|
|
140
|
+
type: e,
|
|
141
|
+
key: r,
|
|
142
|
+
props: t,
|
|
143
|
+
_owner: n
|
|
144
|
+
}, (a !== void 0 ? a : null) !== null ? Object.defineProperty(e, "ref", {
|
|
145
|
+
enumerable: !1,
|
|
146
|
+
get: D
|
|
147
|
+
}) : Object.defineProperty(e, "ref", { enumerable: !1, value: null }), e._store = {}, Object.defineProperty(e._store, "validated", {
|
|
148
|
+
configurable: !1,
|
|
149
|
+
enumerable: !1,
|
|
150
|
+
writable: !0,
|
|
151
|
+
value: 0
|
|
152
|
+
}), Object.defineProperty(e, "_debugInfo", {
|
|
153
|
+
configurable: !1,
|
|
154
|
+
enumerable: !1,
|
|
155
|
+
writable: !0,
|
|
156
|
+
value: null
|
|
157
|
+
}), Object.defineProperty(e, "_debugStack", {
|
|
158
|
+
configurable: !1,
|
|
159
|
+
enumerable: !1,
|
|
160
|
+
writable: !0,
|
|
161
|
+
value: R
|
|
162
|
+
}), Object.defineProperty(e, "_debugTask", {
|
|
163
|
+
configurable: !1,
|
|
164
|
+
enumerable: !1,
|
|
165
|
+
writable: !0,
|
|
166
|
+
value: P
|
|
167
|
+
}), Object.freeze && (Object.freeze(e.props), Object.freeze(e)), e;
|
|
168
|
+
}
|
|
169
|
+
function S(e, r, t, n, R, P) {
|
|
170
|
+
var a = r.children;
|
|
171
|
+
if (a !== void 0)
|
|
172
|
+
if (n)
|
|
173
|
+
if (Z(a)) {
|
|
174
|
+
for (n = 0; n < a.length; n++)
|
|
175
|
+
g(a[n]);
|
|
176
|
+
Object.freeze && Object.freeze(a);
|
|
177
|
+
} else
|
|
178
|
+
console.error(
|
|
179
|
+
"React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead."
|
|
180
|
+
);
|
|
181
|
+
else g(a);
|
|
182
|
+
if (x.call(r, "key")) {
|
|
183
|
+
a = u(e);
|
|
184
|
+
var f = Object.keys(r).filter(function(Q) {
|
|
185
|
+
return Q !== "key";
|
|
186
|
+
});
|
|
187
|
+
n = 0 < f.length ? "{key: someKey, " + f.join(": ..., ") + ": ...}" : "{key: someKey}", Y[a + n] || (f = 0 < f.length ? "{" + f.join(": ..., ") + ": ...}" : "{}", console.error(
|
|
188
|
+
`A props object containing a "key" prop is being spread into JSX:
|
|
189
|
+
let props = %s;
|
|
190
|
+
<%s {...props} />
|
|
191
|
+
React keys must be passed directly to JSX without using spread:
|
|
192
|
+
let props = %s;
|
|
193
|
+
<%s key={someKey} {...props} />`,
|
|
194
|
+
n,
|
|
195
|
+
a,
|
|
196
|
+
f,
|
|
197
|
+
a
|
|
198
|
+
), Y[a + n] = !0);
|
|
199
|
+
}
|
|
200
|
+
if (a = null, t !== void 0 && (l(t), a = "" + t), d(r) && (l(r.key), a = "" + r.key), "key" in r) {
|
|
201
|
+
t = {};
|
|
202
|
+
for (var A in r)
|
|
203
|
+
A !== "key" && (t[A] = r[A]);
|
|
204
|
+
} else t = r;
|
|
205
|
+
return a && m(
|
|
206
|
+
t,
|
|
207
|
+
typeof e == "function" ? e.displayName || e.name || "Unknown" : e
|
|
208
|
+
), L(
|
|
209
|
+
e,
|
|
210
|
+
a,
|
|
211
|
+
t,
|
|
212
|
+
o(),
|
|
213
|
+
R,
|
|
214
|
+
P
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
function g(e) {
|
|
218
|
+
y(e) ? e._store && (e._store.validated = 1) : typeof e == "object" && e !== null && e.$$typeof === T && (e._payload.status === "fulfilled" ? y(e._payload.value) && e._payload.value._store && (e._payload.value._store.validated = 1) : e._store && (e._store.validated = 1));
|
|
219
|
+
}
|
|
220
|
+
function y(e) {
|
|
221
|
+
return typeof e == "object" && e !== null && e.$$typeof === w;
|
|
222
|
+
}
|
|
223
|
+
var v = K, w = /* @__PURE__ */ Symbol.for("react.transitional.element"), M = /* @__PURE__ */ Symbol.for("react.portal"), p = /* @__PURE__ */ Symbol.for("react.fragment"), W = /* @__PURE__ */ Symbol.for("react.strict_mode"), U = /* @__PURE__ */ Symbol.for("react.profiler"), q = /* @__PURE__ */ Symbol.for("react.consumer"), J = /* @__PURE__ */ Symbol.for("react.context"), V = /* @__PURE__ */ Symbol.for("react.forward_ref"), z = /* @__PURE__ */ Symbol.for("react.suspense"), G = /* @__PURE__ */ Symbol.for("react.suspense_list"), X = /* @__PURE__ */ Symbol.for("react.memo"), T = /* @__PURE__ */ Symbol.for("react.lazy"), B = /* @__PURE__ */ Symbol.for("react.activity"), H = /* @__PURE__ */ Symbol.for("react.client.reference"), k = v.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, x = Object.prototype.hasOwnProperty, Z = Array.isArray, O = console.createTask ? console.createTask : function() {
|
|
224
|
+
return null;
|
|
225
|
+
};
|
|
226
|
+
v = {
|
|
227
|
+
react_stack_bottom_frame: function(e) {
|
|
228
|
+
return e();
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
var j, h = {}, N = v.react_stack_bottom_frame.bind(
|
|
232
|
+
v,
|
|
233
|
+
s
|
|
234
|
+
)(), C = O(c(s)), Y = {};
|
|
235
|
+
E.Fragment = p, E.jsx = function(e, r, t) {
|
|
236
|
+
var n = 1e4 > k.recentlyCreatedOwnerStacks++;
|
|
237
|
+
return S(
|
|
238
|
+
e,
|
|
239
|
+
r,
|
|
240
|
+
t,
|
|
241
|
+
!1,
|
|
242
|
+
n ? Error("react-stack-top-frame") : N,
|
|
243
|
+
n ? O(c(e)) : C
|
|
244
|
+
);
|
|
245
|
+
}, E.jsxs = function(e, r, t) {
|
|
246
|
+
var n = 1e4 > k.recentlyCreatedOwnerStacks++;
|
|
247
|
+
return S(
|
|
248
|
+
e,
|
|
249
|
+
r,
|
|
250
|
+
t,
|
|
251
|
+
!0,
|
|
252
|
+
n ? Error("react-stack-top-frame") : N,
|
|
253
|
+
n ? O(c(e)) : C
|
|
254
|
+
);
|
|
255
|
+
};
|
|
256
|
+
})()), E;
|
|
257
|
+
}
|
|
258
|
+
var F;
|
|
259
|
+
function ne() {
|
|
260
|
+
return F || (F = 1, process.env.NODE_ENV === "production" ? b.exports = re() : b.exports = te()), b.exports;
|
|
261
|
+
}
|
|
262
|
+
var ae = ne();
|
|
263
|
+
function ue({
|
|
264
|
+
children: u,
|
|
265
|
+
ripple: i = !0,
|
|
266
|
+
primeConfig: l = {}
|
|
267
|
+
}) {
|
|
268
|
+
const c = {
|
|
269
|
+
ripple: i,
|
|
270
|
+
inputStyle: "outlined",
|
|
271
|
+
...l
|
|
272
|
+
};
|
|
273
|
+
return /* @__PURE__ */ ae.jsx(ee, { value: c, children: u });
|
|
274
|
+
}
|
|
275
|
+
const le = "0.2.1", ce = {
|
|
2
276
|
name: "Yggdrasil",
|
|
3
|
-
version: "0.1
|
|
277
|
+
version: "0.2.1",
|
|
4
278
|
tokens: 727,
|
|
5
|
-
coverage: "
|
|
279
|
+
coverage: "100%",
|
|
6
280
|
modes: ["light", "dark"]
|
|
7
281
|
};
|
|
8
282
|
export {
|
|
9
|
-
|
|
10
|
-
|
|
283
|
+
ue as YggdrasilProvider,
|
|
284
|
+
ce as theme,
|
|
285
|
+
le as version
|
|
11
286
|
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typography Utilities (Optional)
|
|
3
|
+
*
|
|
4
|
+
* This file provides optional typography variables based on Yggdrasil design tokens.
|
|
5
|
+
* PrimeReact components use hardcoded sizing and don't consume these variables.
|
|
6
|
+
*
|
|
7
|
+
* Use these variables in your application code for consistent typography:
|
|
8
|
+
* - Body text: var(--Body-medium-font-size), var(--Body-medium-line-height)
|
|
9
|
+
* - Headings: var(--Heading-large-font-size), var(--Heading-large-line-height)
|
|
10
|
+
* - Titles: var(--Title-medium-font-size), var(--Title-medium-line-height)
|
|
11
|
+
*
|
|
12
|
+
* Font weights available:
|
|
13
|
+
* - 400 (Regular)
|
|
14
|
+
* - 500 (Medium)
|
|
15
|
+
* - 700 (Bold)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
:root {
|
|
19
|
+
/* Body Text */
|
|
20
|
+
--Body-family: 'Roboto', arial, sans-serif;
|
|
21
|
+
--Body-large-font-size: 16px;
|
|
22
|
+
--Body-large-line-height: 28px;
|
|
23
|
+
--Body-medium-font-size: 14px;
|
|
24
|
+
--Body-medium-line-height: 24px;
|
|
25
|
+
--Body-small-font-size: 12px;
|
|
26
|
+
--Body-small-line-height: 20px;
|
|
27
|
+
--Body-weight-regular: 400;
|
|
28
|
+
--Body-weight-medium: 500;
|
|
29
|
+
--Body-weight-bold: 700;
|
|
30
|
+
|
|
31
|
+
/* Button Text */
|
|
32
|
+
--Button-family: 'Roboto', arial, sans-serif;
|
|
33
|
+
--Button-large-font-size: 18px;
|
|
34
|
+
--Button-large-line-height: 24px;
|
|
35
|
+
--Button-medium-font-size: 14px;
|
|
36
|
+
--Button-medium-line-height: 16px;
|
|
37
|
+
--Button-small-font-size: 12px;
|
|
38
|
+
--Button-small-line-height: 16px;
|
|
39
|
+
--Button-weight: 500;
|
|
40
|
+
|
|
41
|
+
/* Headings */
|
|
42
|
+
--Heading-family: 'Roboto', arial, sans-serif;
|
|
43
|
+
--Heading-large-font-size: 24px;
|
|
44
|
+
--Heading-large-line-height: 32px;
|
|
45
|
+
--Heading-medium-font-size: 20px;
|
|
46
|
+
--Heading-medium-line-height: 28px;
|
|
47
|
+
--Heading-small-font-size: 18px;
|
|
48
|
+
--Heading-small-line-height: 24px;
|
|
49
|
+
--Heading-weight: 500;
|
|
50
|
+
|
|
51
|
+
/* Titles */
|
|
52
|
+
--Title-family: 'Roboto', arial, sans-serif;
|
|
53
|
+
--Title-large-font-size: 36px;
|
|
54
|
+
--Title-large-line-height: 40px;
|
|
55
|
+
--Title-medium-font-size: 28px;
|
|
56
|
+
--Title-medium-line-height: 36px;
|
|
57
|
+
--Title-small-font-size: 22px;
|
|
58
|
+
--Title-small-line-height: 30px;
|
|
59
|
+
--Title-weight: 700;
|
|
60
|
+
|
|
61
|
+
/* Labels */
|
|
62
|
+
--Label-family: 'Roboto', arial, sans-serif;
|
|
63
|
+
--Label-large-font-size: 16px;
|
|
64
|
+
--Label-large-line-height: 24px;
|
|
65
|
+
--Label-medium-font-size: 14px;
|
|
66
|
+
--Label-medium-line-height: 20px;
|
|
67
|
+
--Label-small-font-size: 12px;
|
|
68
|
+
--Label-small-line-height: 16px;
|
|
69
|
+
--Label-x-small-font-size: 10px;
|
|
70
|
+
--Label-x-small-line-height: 12px;
|
|
71
|
+
--Label-weight-regular: 400;
|
|
72
|
+
--Label-weight-bold: 700;
|
|
73
|
+
}
|
package/dist/yggdrasil-dark.css
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* For runtime switching, use yggdrasil-adaptive.css
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
@import url('https://fonts.googleapis.com/css2?family=Roboto:
|
|
8
|
+
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap');
|
|
9
9
|
@import "./foundations.css";
|
|
10
10
|
@import "./radius.css";
|
|
11
11
|
@import "./semantic-dark.css";
|
package/dist/yggdrasil-light.css
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* For runtime switching, use yggdrasil-adaptive.css
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
@import url('https://fonts.googleapis.com/css2?family=Roboto:
|
|
8
|
+
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap');
|
|
9
9
|
@import "./foundations.css";
|
|
10
10
|
@import "./radius.css";
|
|
11
11
|
@import "./semantic-light.css";
|
|
@@ -45,7 +45,7 @@ cp -r path/to/prime-yggdrasil/src/theme ./src/design-system
|
|
|
45
45
|
|
|
46
46
|
## 🚀 Integration
|
|
47
47
|
|
|
48
|
-
### 1. Import Theme in Your App
|
|
48
|
+
### 1. Import Theme and Provider in Your App
|
|
49
49
|
|
|
50
50
|
```tsx
|
|
51
51
|
// src/main.tsx or src/App.tsx
|
|
@@ -54,15 +54,73 @@ import 'prime-yggdrasil/yggdrasil-light.css'; // Light theme
|
|
|
54
54
|
import 'prime-yggdrasil/yggdrasil-dark.css'; // Dark theme
|
|
55
55
|
|
|
56
56
|
import 'primeicons/primeicons.css';
|
|
57
|
+
import { YggdrasilProvider } from 'prime-yggdrasil';
|
|
57
58
|
|
|
58
59
|
// Your app code
|
|
59
60
|
import { Button } from 'primereact/button';
|
|
60
61
|
|
|
61
62
|
function App() {
|
|
62
|
-
return
|
|
63
|
+
return (
|
|
64
|
+
<YggdrasilProvider ripple={true}>
|
|
65
|
+
<Button label="Hello Yggdrasil" />
|
|
66
|
+
</YggdrasilProvider>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**YggdrasilProvider Configuration:**
|
|
72
|
+
- `ripple` (default: `true`) - Enable ripple effect on interactive components (buttons, checkboxes, etc.)
|
|
73
|
+
- `primeConfig` - Additional PrimeReact configuration options
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
<YggdrasilProvider
|
|
77
|
+
ripple={true}
|
|
78
|
+
primeConfig={{
|
|
79
|
+
inputStyle: 'outlined', // or 'filled'
|
|
80
|
+
// other PrimeReact config...
|
|
81
|
+
}}
|
|
82
|
+
>
|
|
83
|
+
<App />
|
|
84
|
+
</YggdrasilProvider>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Typography (Optional)
|
|
88
|
+
|
|
89
|
+
Prime Yggdrasil loads Roboto font family by default. If you want to use the Yggdrasil typography system in your custom components:
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
// Import optional typography utilities
|
|
93
|
+
import '@lifeonlars/prime-yggdrasil/typography.css';
|
|
94
|
+
|
|
95
|
+
// Use in your custom components
|
|
96
|
+
function MyComponent() {
|
|
97
|
+
return (
|
|
98
|
+
<div style={{
|
|
99
|
+
fontFamily: 'var(--Body-family)',
|
|
100
|
+
fontSize: 'var(--Body-medium-font-size)',
|
|
101
|
+
lineHeight: 'var(--Body-medium-line-height)',
|
|
102
|
+
fontWeight: 'var(--Body-weight-regular)'
|
|
103
|
+
}}>
|
|
104
|
+
Custom content with Yggdrasil typography
|
|
105
|
+
</div>
|
|
106
|
+
);
|
|
63
107
|
}
|
|
64
108
|
```
|
|
65
109
|
|
|
110
|
+
**Note:** PrimeReact components have their own typography sizing and don't consume these variables. Use these tokens only for custom application code outside of PrimeReact components.
|
|
111
|
+
|
|
112
|
+
**Available font weights:**
|
|
113
|
+
- 400 (Regular)
|
|
114
|
+
- 500 (Medium)
|
|
115
|
+
- 700 (Bold)
|
|
116
|
+
|
|
117
|
+
**Available typography categories:**
|
|
118
|
+
- `--Body-*` - Body text styles (small, medium, large)
|
|
119
|
+
- `--Heading-*` - Heading styles (small, medium, large)
|
|
120
|
+
- `--Title-*` - Title styles (small, medium, large)
|
|
121
|
+
- `--Button-*` - Button text styles (small, medium, large)
|
|
122
|
+
- `--Label-*` - Label styles (x-small, small, medium, large)
|
|
123
|
+
|
|
66
124
|
### 2. Configure Your Build Tool
|
|
67
125
|
|
|
68
126
|
#### Vite (Recommended)
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
# Storybook Testing Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how to test components in Prime Yggdrasil using Storybook's Vitest addon.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Prime Yggdrasil uses `@storybook/addon-vitest` for component testing. This allows you to:
|
|
8
|
+
- Write tests alongside your stories
|
|
9
|
+
- Test components in a real browser environment
|
|
10
|
+
- Use Vitest's familiar testing API
|
|
11
|
+
- Run tests in watch mode for development
|
|
12
|
+
|
|
13
|
+
## Setup
|
|
14
|
+
|
|
15
|
+
The testing infrastructure is already configured:
|
|
16
|
+
|
|
17
|
+
### Files
|
|
18
|
+
- `.storybook/vitest.config.ts` - Vitest configuration for Storybook
|
|
19
|
+
- `.storybook/vitest.setup.ts` - Test setup and Storybook project annotations
|
|
20
|
+
- `package.json` - Test scripts
|
|
21
|
+
|
|
22
|
+
### Scripts
|
|
23
|
+
```bash
|
|
24
|
+
# Run all Storybook tests once
|
|
25
|
+
npm run test:storybook
|
|
26
|
+
|
|
27
|
+
# Run tests in watch mode (auto-rerun on changes)
|
|
28
|
+
npm run test:storybook:watch
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Writing Tests
|
|
32
|
+
|
|
33
|
+
### Basic Test Structure
|
|
34
|
+
|
|
35
|
+
Tests are written using Vitest's `expect` API inside your story files:
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { test, expect } from '@storybook/test';
|
|
39
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
40
|
+
import { Button } from 'primereact/button';
|
|
41
|
+
|
|
42
|
+
const meta = {
|
|
43
|
+
title: 'Button/Button',
|
|
44
|
+
component: Button,
|
|
45
|
+
} satisfies Meta<typeof Button>;
|
|
46
|
+
|
|
47
|
+
export default meta;
|
|
48
|
+
type Story = StoryObj<typeof meta>;
|
|
49
|
+
|
|
50
|
+
export const Primary: Story = {
|
|
51
|
+
args: {
|
|
52
|
+
label: 'Click me',
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Test the Primary story
|
|
57
|
+
test('Primary button renders correctly', async ({ mount }) => {
|
|
58
|
+
const component = await mount(<Primary.component {...Primary.args} />);
|
|
59
|
+
const button = await component.getByRole('button');
|
|
60
|
+
|
|
61
|
+
await expect(button).toBeInTheDocument();
|
|
62
|
+
await expect(button).toHaveTextContent('Click me');
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Testing Interactive Components
|
|
67
|
+
|
|
68
|
+
For components with state (like Checkbox, RadioButton, etc.):
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
import { test, expect } from '@storybook/test';
|
|
72
|
+
import { userEvent } from '@storybook/test';
|
|
73
|
+
|
|
74
|
+
export const Default: Story = {
|
|
75
|
+
render: () => {
|
|
76
|
+
const [checked, setChecked] = useState(false);
|
|
77
|
+
return (
|
|
78
|
+
<Checkbox checked={checked} onChange={(e) => setChecked(e.checked)} />
|
|
79
|
+
);
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
test('Checkbox toggles on click', async ({ mount, page }) => {
|
|
84
|
+
await mount(<Default.component />);
|
|
85
|
+
|
|
86
|
+
const checkbox = page.getByRole('checkbox');
|
|
87
|
+
|
|
88
|
+
// Initially unchecked
|
|
89
|
+
await expect(checkbox).not.toBeChecked();
|
|
90
|
+
|
|
91
|
+
// Click to check
|
|
92
|
+
await userEvent.click(checkbox);
|
|
93
|
+
await expect(checkbox).toBeChecked();
|
|
94
|
+
|
|
95
|
+
// Click again to uncheck
|
|
96
|
+
await userEvent.click(checkbox);
|
|
97
|
+
await expect(checkbox).not.toBeChecked();
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Testing Theme Switching
|
|
102
|
+
|
|
103
|
+
Test components in both light and dark modes:
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
test('Button renders in dark mode', async ({ mount, page }) => {
|
|
107
|
+
// Set dark mode
|
|
108
|
+
await page.evaluate(() => {
|
|
109
|
+
document.documentElement.setAttribute('data-theme', 'dark');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
await mount(<Primary.component {...Primary.args} />);
|
|
113
|
+
|
|
114
|
+
const button = page.getByRole('button');
|
|
115
|
+
const bgColor = await button.evaluate((el) =>
|
|
116
|
+
getComputedStyle(el).backgroundColor
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
// Dark mode should have darker background
|
|
120
|
+
await expect(bgColor).toBeTruthy();
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Testing Accessibility
|
|
125
|
+
|
|
126
|
+
Use built-in a11y assertions:
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
import { test, expect } from '@storybook/test';
|
|
130
|
+
|
|
131
|
+
test('Button is accessible', async ({ mount, page }) => {
|
|
132
|
+
await mount(<Primary.component {...Primary.args} />);
|
|
133
|
+
|
|
134
|
+
const button = page.getByRole('button');
|
|
135
|
+
|
|
136
|
+
// Check ARIA attributes
|
|
137
|
+
await expect(button).toBeEnabled();
|
|
138
|
+
await expect(button).toBeVisible();
|
|
139
|
+
await expect(button).toHaveAccessibleName('Click me');
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Best Practices
|
|
144
|
+
|
|
145
|
+
### 1. Test One Concern Per Test
|
|
146
|
+
```tsx
|
|
147
|
+
// Good
|
|
148
|
+
test('renders with label', async ({ mount }) => { /* ... */ });
|
|
149
|
+
test('responds to click', async ({ mount }) => { /* ... */ });
|
|
150
|
+
|
|
151
|
+
// Avoid
|
|
152
|
+
test('renders and clicks', async ({ mount }) => { /* ... */ });
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### 2. Use Semantic Queries
|
|
156
|
+
```tsx
|
|
157
|
+
// Good - semantic and accessible
|
|
158
|
+
const button = page.getByRole('button', { name: 'Submit' });
|
|
159
|
+
const heading = page.getByRole('heading', { level: 1 });
|
|
160
|
+
|
|
161
|
+
// Avoid - fragile
|
|
162
|
+
const button = page.locator('.p-button');
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 3. Test User Behavior, Not Implementation
|
|
166
|
+
```tsx
|
|
167
|
+
// Good - tests what user sees/does
|
|
168
|
+
test('shows error message on invalid input', async ({ mount, page }) => {
|
|
169
|
+
await mount(<Form />);
|
|
170
|
+
await page.getByRole('button', { name: 'Submit' }).click();
|
|
171
|
+
await expect(page.getByText('Required field')).toBeVisible();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Avoid - tests implementation details
|
|
175
|
+
test('sets error state to true', async ({ mount }) => {
|
|
176
|
+
const { rerender } = await mount(<Form />);
|
|
177
|
+
// Don't test internal state
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### 4. Keep Tests Fast
|
|
182
|
+
- Use `mount` instead of full page loads
|
|
183
|
+
- Mock external API calls
|
|
184
|
+
- Avoid unnecessary `waitFor` delays
|
|
185
|
+
|
|
186
|
+
### 5. Test Across Themes
|
|
187
|
+
```tsx
|
|
188
|
+
test.each(['light', 'dark'])('renders in %s mode', async (theme, { mount, page }) => {
|
|
189
|
+
await page.evaluate((t) => {
|
|
190
|
+
document.documentElement.setAttribute('data-theme', t);
|
|
191
|
+
}, theme);
|
|
192
|
+
|
|
193
|
+
await mount(<Component />);
|
|
194
|
+
// Test rendering...
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Common Patterns
|
|
199
|
+
|
|
200
|
+
### Testing Forms
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
test('form submission', async ({ mount, page }) => {
|
|
204
|
+
await mount(<FormStory />);
|
|
205
|
+
|
|
206
|
+
// Fill in fields
|
|
207
|
+
await page.getByLabel('Email').fill('user@example.com');
|
|
208
|
+
await page.getByLabel('Password').fill('password123');
|
|
209
|
+
|
|
210
|
+
// Submit
|
|
211
|
+
await page.getByRole('button', { name: 'Sign In' }).click();
|
|
212
|
+
|
|
213
|
+
// Verify result
|
|
214
|
+
await expect(page.getByText('Welcome!')).toBeVisible();
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Testing Dropdowns
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
test('dropdown selection', async ({ mount, page }) => {
|
|
222
|
+
await mount(<DropdownStory />);
|
|
223
|
+
|
|
224
|
+
const dropdown = page.getByRole('combobox');
|
|
225
|
+
await dropdown.click();
|
|
226
|
+
|
|
227
|
+
// Select option
|
|
228
|
+
await page.getByRole('option', { name: 'New York' }).click();
|
|
229
|
+
|
|
230
|
+
// Verify selection
|
|
231
|
+
await expect(dropdown).toHaveValue('NY');
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Testing Modals/Dialogs
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
test('dialog opens and closes', async ({ mount, page }) => {
|
|
239
|
+
await mount(<DialogStory />);
|
|
240
|
+
|
|
241
|
+
// Open dialog
|
|
242
|
+
await page.getByRole('button', { name: 'Open' }).click();
|
|
243
|
+
|
|
244
|
+
const dialog = page.getByRole('dialog');
|
|
245
|
+
await expect(dialog).toBeVisible();
|
|
246
|
+
|
|
247
|
+
// Close dialog
|
|
248
|
+
await dialog.getByRole('button', { name: 'Close' }).click();
|
|
249
|
+
await expect(dialog).not.toBeVisible();
|
|
250
|
+
});
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Debugging Tests
|
|
254
|
+
|
|
255
|
+
### Run in headed mode
|
|
256
|
+
```bash
|
|
257
|
+
# See the browser while tests run
|
|
258
|
+
npm run test:storybook:watch
|
|
259
|
+
# Then in the Vitest UI, enable "headed" mode
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Use page.pause()
|
|
263
|
+
```tsx
|
|
264
|
+
test('debug test', async ({ mount, page }) => {
|
|
265
|
+
await mount(<Component />);
|
|
266
|
+
|
|
267
|
+
await page.pause(); // Opens Playwright Inspector
|
|
268
|
+
|
|
269
|
+
// Test continues...
|
|
270
|
+
});
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Screenshot on failure
|
|
274
|
+
```tsx
|
|
275
|
+
test('visual test', async ({ mount, page }) => {
|
|
276
|
+
await mount(<Component />);
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
await expect(page.getByRole('button')).toBeVisible();
|
|
280
|
+
} catch (error) {
|
|
281
|
+
await page.screenshot({ path: 'test-failure.png' });
|
|
282
|
+
throw error;
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Continuous Integration
|
|
288
|
+
|
|
289
|
+
For CI/CD pipelines:
|
|
290
|
+
|
|
291
|
+
```yaml
|
|
292
|
+
# .github/workflows/test.yml
|
|
293
|
+
- name: Install dependencies
|
|
294
|
+
run: npm ci
|
|
295
|
+
|
|
296
|
+
- name: Install Playwright browsers
|
|
297
|
+
run: npx playwright install --with-deps chromium
|
|
298
|
+
|
|
299
|
+
- name: Run Storybook tests
|
|
300
|
+
run: npm run test:storybook
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Troubleshooting
|
|
304
|
+
|
|
305
|
+
### "Failed to start test runner process"
|
|
306
|
+
|
|
307
|
+
This usually means:
|
|
308
|
+
1. Playwright browsers aren't installed: `npx playwright install chromium`
|
|
309
|
+
2. Port conflict: Close other Storybook instances
|
|
310
|
+
3. Timeout: Increase timeout in vitest.config.ts
|
|
311
|
+
|
|
312
|
+
### Tests timing out
|
|
313
|
+
|
|
314
|
+
Increase test timeout:
|
|
315
|
+
```ts
|
|
316
|
+
// .storybook/vitest.config.ts
|
|
317
|
+
export default defineConfig({
|
|
318
|
+
test: {
|
|
319
|
+
testTimeout: 30000, // 30 seconds
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Theme styles not applying
|
|
325
|
+
|
|
326
|
+
Ensure YggdrasilProvider is wrapping your component:
|
|
327
|
+
```tsx
|
|
328
|
+
// In your story
|
|
329
|
+
export const Default: Story = {
|
|
330
|
+
decorators: [
|
|
331
|
+
(Story) => (
|
|
332
|
+
<YggdrasilProvider ripple={true}>
|
|
333
|
+
<Story />
|
|
334
|
+
</YggdrasilProvider>
|
|
335
|
+
),
|
|
336
|
+
],
|
|
337
|
+
};
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Resources
|
|
341
|
+
|
|
342
|
+
- [Storybook Test Addon Docs](https://storybook.js.org/docs/writing-tests/test-addon)
|
|
343
|
+
- [Vitest API](https://vitest.dev/api/)
|
|
344
|
+
- [Playwright Test API](https://playwright.dev/docs/api/class-test)
|
|
345
|
+
- [Testing Library Queries](https://testing-library.com/docs/queries/about)
|
|
346
|
+
|
|
347
|
+
## Next Steps
|
|
348
|
+
|
|
349
|
+
1. Add tests to existing stories
|
|
350
|
+
2. Set up CI pipeline to run tests
|
|
351
|
+
3. Add visual regression testing with Chromatic
|
|
352
|
+
4. Configure test coverage reporting
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lifeonlars/prime-yggdrasil",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AI-agent-friendly PrimeReact design system for component-driven development with semantic tokens and dark mode support",
|
|
6
6
|
"keywords": [
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"./yggdrasil-dark.css": "./dist/yggdrasil-dark.css",
|
|
37
37
|
"./theme.css": "./dist/prime-yggdrasil.css",
|
|
38
38
|
"./foundations.css": "./dist/foundations.css",
|
|
39
|
+
"./typography.css": "./dist/typography.css",
|
|
39
40
|
"./docs/*": "./docs/*"
|
|
40
41
|
},
|
|
41
42
|
"files": [
|
|
@@ -53,6 +54,8 @@
|
|
|
53
54
|
"preview": "vite preview",
|
|
54
55
|
"test": "vitest run",
|
|
55
56
|
"test:watch": "vitest",
|
|
57
|
+
"test:storybook": "test-storybook",
|
|
58
|
+
"test:storybook:watch": "test-storybook --watch",
|
|
56
59
|
"test:contrast": "node scripts/test-contrast.js",
|
|
57
60
|
"test:contrast:vitest": "vitest run tests/contrast.test.ts",
|
|
58
61
|
"test:contrast:watch": "vitest tests/contrast.test.ts",
|
|
@@ -75,11 +78,15 @@
|
|
|
75
78
|
"devDependencies": {
|
|
76
79
|
"@chromatic-com/storybook": "^4.1.3",
|
|
77
80
|
"@eslint/js": "^9.39.1",
|
|
78
|
-
"@storybook/addon-a11y": "^10.1.
|
|
79
|
-
"@storybook/addon-docs": "^10.1.
|
|
80
|
-
"@storybook/addon-onboarding": "^10.1.
|
|
81
|
-
"@storybook/addon-vitest": "^10.1.
|
|
82
|
-
"@storybook/react-vite": "^10.1.
|
|
81
|
+
"@storybook/addon-a11y": "^10.1.11",
|
|
82
|
+
"@storybook/addon-docs": "^10.1.11",
|
|
83
|
+
"@storybook/addon-onboarding": "^10.1.11",
|
|
84
|
+
"@storybook/addon-vitest": "^10.1.11",
|
|
85
|
+
"@storybook/react-vite": "^10.1.11",
|
|
86
|
+
"@storybook/test-runner": "^0.24.2",
|
|
87
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
88
|
+
"@testing-library/react": "^16.3.1",
|
|
89
|
+
"@testing-library/user-event": "^14.6.1",
|
|
83
90
|
"@types/node": "^24.10.1",
|
|
84
91
|
"@types/react": "^19.2.5",
|
|
85
92
|
"@types/react-dom": "^19.2.3",
|
|
@@ -93,8 +100,9 @@
|
|
|
93
100
|
"eslint": "^9.39.1",
|
|
94
101
|
"eslint-plugin-react-hooks": "^7.0.1",
|
|
95
102
|
"eslint-plugin-react-refresh": "^0.4.24",
|
|
96
|
-
"eslint-plugin-storybook": "^10.1.
|
|
103
|
+
"eslint-plugin-storybook": "^10.1.11",
|
|
97
104
|
"globals": "^16.5.0",
|
|
105
|
+
"jsdom": "^27.4.0",
|
|
98
106
|
"playwright": "^1.57.0",
|
|
99
107
|
"primeflex": "^4.0.0",
|
|
100
108
|
"primeicons": "^7.0.0",
|
|
@@ -102,7 +110,7 @@
|
|
|
102
110
|
"react": "^19.2.0",
|
|
103
111
|
"react-dom": "^19.2.0",
|
|
104
112
|
"sass": "^1.97.0",
|
|
105
|
-
"storybook": "^10.1.
|
|
113
|
+
"storybook": "^10.1.11",
|
|
106
114
|
"stylelint": "^16.26.1",
|
|
107
115
|
"stylelint-config-standard": "^39.0.1",
|
|
108
116
|
"typescript": "~5.9.3",
|