@adobe-commerce/elsie 1.0.1-alpha04151330 → 1.1.0-alpha2345
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/__mocks__/svg.js +2 -1
- package/bin/builders/build/index.js +1 -2
- package/bin/builders/generate/api/index.js +9 -0
- package/bin/builders/generate/api/templates/function.js +11 -2
- package/bin/builders/generate/api/templates/index.js +11 -2
- package/bin/builders/generate/api/templates/story.js +11 -2
- package/bin/builders/generate/api/templates/unit-test.js +11 -2
- package/bin/builders/generate/component/index.js +9 -0
- package/bin/builders/generate/component/templates/Component.js +22 -4
- package/bin/builders/generate/component/templates/css.js +11 -2
- package/bin/builders/generate/component/templates/index.js +11 -2
- package/bin/builders/generate/component/templates/stories.js +11 -2
- package/bin/builders/generate/component/templates/unit-test.js +11 -2
- package/bin/builders/generate/config/index.js +9 -0
- package/bin/builders/generate/config/templates/elsie.js +10 -1
- package/bin/builders/generate/container/index.js +9 -0
- package/bin/builders/generate/container/templates/Component.js +11 -2
- package/bin/builders/generate/container/templates/index.js +11 -2
- package/bin/builders/generate/container/templates/stories.js +11 -2
- package/bin/builders/generate/container/templates/unit-test.js +11 -2
- package/bin/builders/generate/index.js +9 -0
- package/bin/builders/lint/index.js +1 -1
- package/bin/builders/storybook/index.js +1 -1
- package/bin/builders/test/index.js +1 -1
- package/bin/lib/cli.js +16 -1
- package/package.json +2 -2
- package/src/components/Header/Header.tsx +2 -2
- package/src/components/ImageSwatch/ImageSwatch.css +10 -0
- package/src/components/ImageSwatch/ImageSwatch.stories.tsx +53 -4
- package/src/components/ImageSwatch/ImageSwatch.tsx +44 -9
- package/src/components/Incrementer/Incrementer.tsx +10 -1
- package/src/components/Tag/Tag.stories.tsx +1 -0
- package/src/docs/slots.mdx +21 -3
- package/src/lib/slot.tsx +30 -15
package/__mocks__/svg.js
CHANGED
|
@@ -3,8 +3,7 @@ const path = require('path');
|
|
|
3
3
|
module.exports = async function generateResourceBuilder({ argv }) {
|
|
4
4
|
const { build } = await import('vite');
|
|
5
5
|
|
|
6
|
-
const configFile =
|
|
7
|
-
argv?.config ?? path.resolve(__dirname, '../../../config/vite.mjs');
|
|
6
|
+
const configFile = argv?.config ?? path.resolve(__dirname, '../../../config/vite.mjs');
|
|
8
7
|
|
|
9
8
|
const outDir = argv?.outDir ?? 'dist';
|
|
10
9
|
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/********************************************************************
|
|
2
|
+
* Copyright 2025 Adobe
|
|
3
|
+
* All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
|
+
*******************************************************************/
|
|
9
|
+
|
|
1
10
|
const path = require('path');
|
|
2
11
|
const fs = require('fs');
|
|
3
12
|
const writeFile = require('../../../lib/write-file');
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
const { stripIndent } = require('common-tags');
|
|
2
2
|
|
|
3
3
|
module.exports = ({ basename }) => {
|
|
4
|
-
return
|
|
4
|
+
return `/********************************************************************
|
|
5
|
+
* Copyright 2025 Adobe
|
|
6
|
+
* All Rights Reserved.
|
|
7
|
+
*
|
|
8
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
9
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
10
|
+
* accompanying it.
|
|
11
|
+
*******************************************************************/
|
|
12
|
+
|
|
13
|
+
${stripIndent`
|
|
5
14
|
export const ${basename} = () => {
|
|
6
15
|
return 'Howdy!';
|
|
7
16
|
}
|
|
8
|
-
|
|
17
|
+
`}\n`;
|
|
9
18
|
};
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
const { source } = require('common-tags');
|
|
2
2
|
|
|
3
3
|
module.exports = ({ basename, importPath = '.' }) => {
|
|
4
|
-
return
|
|
4
|
+
return `/********************************************************************
|
|
5
|
+
* Copyright 2025 Adobe
|
|
6
|
+
* All Rights Reserved.
|
|
7
|
+
*
|
|
8
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
9
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
10
|
+
* accompanying it.
|
|
11
|
+
*******************************************************************/
|
|
12
|
+
|
|
13
|
+
${source`
|
|
5
14
|
export * from '${importPath}/${basename}';
|
|
6
|
-
|
|
15
|
+
`}\n`;
|
|
7
16
|
};
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
const { source } = require('common-tags');
|
|
2
2
|
|
|
3
3
|
module.exports = ({ pathname, basename, importPath = '.' }) => {
|
|
4
|
-
return
|
|
4
|
+
return `/********************************************************************
|
|
5
|
+
* Copyright 2025 Adobe
|
|
6
|
+
* All Rights Reserved.
|
|
7
|
+
*
|
|
8
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
9
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
10
|
+
* accompanying it.
|
|
11
|
+
*******************************************************************/
|
|
12
|
+
|
|
13
|
+
${source`
|
|
5
14
|
import { Meta } from '@storybook/blocks';
|
|
6
15
|
|
|
7
16
|
<Meta title="API/${pathname}" />
|
|
@@ -19,5 +28,5 @@ module.exports = ({ pathname, basename, importPath = '.' }) => {
|
|
|
19
28
|
\`\`\`ts
|
|
20
29
|
${basename}();
|
|
21
30
|
\`\`\`
|
|
22
|
-
|
|
31
|
+
`}\n`;
|
|
23
32
|
};
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
const { source } = require('common-tags');
|
|
2
2
|
|
|
3
3
|
module.exports = ({ name, basename, pathname, importPath = '.' }) => {
|
|
4
|
-
return
|
|
4
|
+
return `/********************************************************************
|
|
5
|
+
* Copyright 2025 Adobe
|
|
6
|
+
* All Rights Reserved.
|
|
7
|
+
*
|
|
8
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
9
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
10
|
+
* accompanying it.
|
|
11
|
+
*******************************************************************/
|
|
12
|
+
|
|
13
|
+
${source`
|
|
5
14
|
import { ${basename} } from '${importPath}';
|
|
6
15
|
|
|
7
16
|
describe('${name}/api/${pathname}', () => {
|
|
@@ -11,5 +20,5 @@ module.exports = ({ name, basename, pathname, importPath = '.' }) => {
|
|
|
11
20
|
expect(value).toEqual('Howdy!');
|
|
12
21
|
});
|
|
13
22
|
});
|
|
14
|
-
|
|
23
|
+
`}\n`;
|
|
15
24
|
};
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/********************************************************************
|
|
2
|
+
* Copyright 2025 Adobe
|
|
3
|
+
* All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
|
+
*******************************************************************/
|
|
9
|
+
|
|
1
10
|
const path = require('path');
|
|
2
11
|
const fs = require('fs');
|
|
3
12
|
const writeFile = require('../../../lib/write-file');
|
|
@@ -5,7 +5,16 @@ module.exports = ({ basename, importPath = '.', skipCSS, cssPrefix }) => {
|
|
|
5
5
|
const _name = hyphenatedName(basename);
|
|
6
6
|
|
|
7
7
|
if (skipCSS)
|
|
8
|
-
return
|
|
8
|
+
return `/********************************************************************
|
|
9
|
+
* Copyright 2025 Adobe
|
|
10
|
+
* All Rights Reserved.
|
|
11
|
+
*
|
|
12
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
13
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
14
|
+
* accompanying it.
|
|
15
|
+
*******************************************************************/
|
|
16
|
+
|
|
17
|
+
${stripIndent`
|
|
9
18
|
import { FunctionComponent } from 'preact';
|
|
10
19
|
import { HTMLAttributes } from 'preact/compat';
|
|
11
20
|
|
|
@@ -18,9 +27,18 @@ module.exports = ({ basename, importPath = '.', skipCSS, cssPrefix }) => {
|
|
|
18
27
|
</div>
|
|
19
28
|
);
|
|
20
29
|
};
|
|
21
|
-
|
|
30
|
+
`}\n`;
|
|
22
31
|
|
|
23
|
-
return
|
|
32
|
+
return `/********************************************************************
|
|
33
|
+
* Copyright 2025 Adobe
|
|
34
|
+
* All Rights Reserved.
|
|
35
|
+
*
|
|
36
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
37
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
38
|
+
* accompanying it.
|
|
39
|
+
*******************************************************************/
|
|
40
|
+
|
|
41
|
+
${stripIndent`
|
|
24
42
|
import { FunctionComponent } from 'preact';
|
|
25
43
|
import { HTMLAttributes } from 'preact/compat';
|
|
26
44
|
import { classes } from '@adobe-commerce/elsie/lib';
|
|
@@ -39,5 +57,5 @@ module.exports = ({ basename, importPath = '.', skipCSS, cssPrefix }) => {
|
|
|
39
57
|
</div>
|
|
40
58
|
);
|
|
41
59
|
};
|
|
42
|
-
|
|
60
|
+
`}\n`;
|
|
43
61
|
};
|
|
@@ -4,7 +4,16 @@ const { hyphenatedName } = require('../../../../lib/string');
|
|
|
4
4
|
module.exports = ({ basename, cssPrefix }) => {
|
|
5
5
|
const _name = hyphenatedName(basename);
|
|
6
6
|
|
|
7
|
-
return
|
|
7
|
+
return `/********************************************************************
|
|
8
|
+
* Copyright 2025 Adobe
|
|
9
|
+
* All Rights Reserved.
|
|
10
|
+
*
|
|
11
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
12
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
13
|
+
* accompanying it.
|
|
14
|
+
*******************************************************************/
|
|
15
|
+
|
|
16
|
+
${source`
|
|
8
17
|
/* https://cssguidelin.es/#bem-like-naming */
|
|
9
18
|
|
|
10
19
|
/* .${cssPrefix}${_name} { } */
|
|
@@ -20,5 +29,5 @@ module.exports = ({ basename, cssPrefix }) => {
|
|
|
20
29
|
|
|
21
30
|
/* XXlarge (large laptops and desktops, 1920px and up) */
|
|
22
31
|
/* @media only screen and (min-width: 1920px) { } */
|
|
23
|
-
|
|
32
|
+
`}\n`;
|
|
24
33
|
};
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
const { source } = require('common-tags');
|
|
2
2
|
|
|
3
3
|
module.exports = ({ basename, importPath = '.' }) => {
|
|
4
|
-
return
|
|
4
|
+
return `/********************************************************************
|
|
5
|
+
* Copyright 2025 Adobe
|
|
6
|
+
* All Rights Reserved.
|
|
7
|
+
*
|
|
8
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
9
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
10
|
+
* accompanying it.
|
|
11
|
+
*******************************************************************/
|
|
12
|
+
|
|
13
|
+
${source`
|
|
5
14
|
export * from '${importPath}/${basename}';
|
|
6
15
|
export { ${basename} as default } from '${importPath}/${basename}';
|
|
7
|
-
|
|
16
|
+
`}\n`;
|
|
8
17
|
};
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
const { source } = require('common-tags');
|
|
2
2
|
|
|
3
3
|
module.exports = ({ name, pathname, basename, group, importPath = '.' }) => {
|
|
4
|
-
return
|
|
4
|
+
return `/********************************************************************
|
|
5
|
+
* Copyright 2025 Adobe
|
|
6
|
+
* All Rights Reserved.
|
|
7
|
+
*
|
|
8
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
9
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
10
|
+
* accompanying it.
|
|
11
|
+
*******************************************************************/
|
|
12
|
+
|
|
13
|
+
${source`
|
|
5
14
|
// https://storybook.js.org/docs/7.0/preact/writing-stories/introduction
|
|
6
15
|
import type { Meta, StoryObj } from '@storybook/preact';
|
|
7
16
|
import { ${basename} as component, ${basename}Props } from '${importPath}';
|
|
@@ -42,5 +51,5 @@ module.exports = ({ name, pathname, basename, group, importPath = '.' }) => {
|
|
|
42
51
|
children: "👋 Hello from your new ${basename} story!",
|
|
43
52
|
},
|
|
44
53
|
};
|
|
45
|
-
|
|
54
|
+
`}\n`;
|
|
46
55
|
};
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
const { source } = require('common-tags');
|
|
2
2
|
|
|
3
3
|
module.exports = ({ name, basename, pathname, group, importPath = '.' }) => {
|
|
4
|
-
return
|
|
4
|
+
return `/********************************************************************
|
|
5
|
+
* Copyright 2025 Adobe
|
|
6
|
+
* All Rights Reserved.
|
|
7
|
+
*
|
|
8
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
9
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
10
|
+
* accompanying it.
|
|
11
|
+
*******************************************************************/
|
|
12
|
+
|
|
13
|
+
${source`
|
|
5
14
|
/** https://preactjs.com/guide/v10/preact-testing-library/ */
|
|
6
15
|
|
|
7
16
|
import { render } from '@adobe-commerce/elsie/lib/tests';
|
|
@@ -15,5 +24,5 @@ module.exports = ({ name, basename, pathname, group, importPath = '.' }) => {
|
|
|
15
24
|
expect(!!container).toEqual(true);
|
|
16
25
|
});
|
|
17
26
|
});
|
|
18
|
-
|
|
27
|
+
`}\n`;
|
|
19
28
|
};
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/********************************************************************
|
|
2
|
+
* Copyright 2025 Adobe
|
|
3
|
+
* All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
|
+
*******************************************************************/
|
|
9
|
+
|
|
1
10
|
const path = require('path');
|
|
2
11
|
const fs = require('fs/promises');
|
|
3
12
|
const m = require('../../../lib/log-message');
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
const { source } = require('common-tags');
|
|
2
2
|
|
|
3
3
|
module.exports = ({ name, importPath }) =>
|
|
4
|
-
|
|
4
|
+
`/********************************************************************
|
|
5
|
+
* Copyright 2025 Adobe
|
|
6
|
+
* All Rights Reserved.
|
|
7
|
+
*
|
|
8
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
9
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
10
|
+
* accompanying it.
|
|
11
|
+
*******************************************************************/
|
|
12
|
+
|
|
13
|
+
${source`
|
|
5
14
|
module.exports = {
|
|
6
15
|
name: '${name}',
|
|
7
16
|
api: {
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/********************************************************************
|
|
2
|
+
* Copyright 2025 Adobe
|
|
3
|
+
* All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
|
+
*******************************************************************/
|
|
9
|
+
|
|
1
10
|
const path = require('path');
|
|
2
11
|
const fs = require('fs');
|
|
3
12
|
const writeFile = require('../../../lib/write-file');
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
const { stripIndent } = require('common-tags');
|
|
2
2
|
|
|
3
3
|
module.exports = ({ basename }) => {
|
|
4
|
-
return
|
|
4
|
+
return `/********************************************************************
|
|
5
|
+
* Copyright 2025 Adobe
|
|
6
|
+
* All Rights Reserved.
|
|
7
|
+
*
|
|
8
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
9
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
10
|
+
* accompanying it.
|
|
11
|
+
*******************************************************************/
|
|
12
|
+
|
|
13
|
+
${stripIndent`
|
|
5
14
|
import { HTMLAttributes } from 'preact/compat';
|
|
6
15
|
import { Container } from '@adobe-commerce/elsie/lib';
|
|
7
16
|
|
|
@@ -14,5 +23,5 @@ module.exports = ({ basename }) => {
|
|
|
14
23
|
</div>
|
|
15
24
|
);
|
|
16
25
|
};
|
|
17
|
-
|
|
26
|
+
`}\n`;
|
|
18
27
|
};
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
const { source } = require('common-tags');
|
|
2
2
|
|
|
3
3
|
module.exports = ({ basename, importPath = '.' }) => {
|
|
4
|
-
return
|
|
4
|
+
return `/********************************************************************
|
|
5
|
+
* Copyright 2025 Adobe
|
|
6
|
+
* All Rights Reserved.
|
|
7
|
+
*
|
|
8
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
9
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
10
|
+
* accompanying it.
|
|
11
|
+
*******************************************************************/
|
|
12
|
+
|
|
13
|
+
${source`
|
|
5
14
|
export * from '${importPath}/${basename}';
|
|
6
15
|
export { ${basename} as default } from '${importPath}/${basename}';
|
|
7
|
-
|
|
16
|
+
`}\n`;
|
|
8
17
|
};
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
const { source } = require('common-tags');
|
|
2
2
|
|
|
3
3
|
module.exports = ({ pathname, basename, importPath = '.' }) => {
|
|
4
|
-
return
|
|
4
|
+
return `/********************************************************************
|
|
5
|
+
* Copyright 2025 Adobe
|
|
6
|
+
* All Rights Reserved.
|
|
7
|
+
*
|
|
8
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
9
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
10
|
+
* accompanying it.
|
|
11
|
+
*******************************************************************/
|
|
12
|
+
|
|
13
|
+
${source`
|
|
5
14
|
// https://storybook.js.org/docs/7.0/preact/writing-stories/introduction
|
|
6
15
|
import type { Meta, StoryObj } from '@storybook/preact';
|
|
7
16
|
import { ${basename} as component, ${basename}Props } from '${importPath}';
|
|
@@ -30,5 +39,5 @@ module.exports = ({ pathname, basename, importPath = '.' }) => {
|
|
|
30
39
|
children: "👋 Howdy, I'm Howdy!",
|
|
31
40
|
},
|
|
32
41
|
};
|
|
33
|
-
|
|
42
|
+
`}\n`;
|
|
34
43
|
};
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
const { source } = require('common-tags');
|
|
2
2
|
|
|
3
3
|
module.exports = ({ name, basename, pathname, importPath = '.' }) => {
|
|
4
|
-
return
|
|
4
|
+
return `/********************************************************************
|
|
5
|
+
* Copyright 2025 Adobe
|
|
6
|
+
* All Rights Reserved.
|
|
7
|
+
*
|
|
8
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
9
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
10
|
+
* accompanying it.
|
|
11
|
+
*******************************************************************/
|
|
12
|
+
|
|
13
|
+
${source`
|
|
5
14
|
/** https://preactjs.com/guide/v10/preact-testing-library/ */
|
|
6
15
|
|
|
7
16
|
import { render } from '@adobe-commerce/elsie/lib/tests';
|
|
@@ -15,5 +24,5 @@ module.exports = ({ name, basename, pathname, importPath = '.' }) => {
|
|
|
15
24
|
expect(!!container).toEqual(true);
|
|
16
25
|
});
|
|
17
26
|
});
|
|
18
|
-
|
|
27
|
+
`}\n`;
|
|
19
28
|
};
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/********************************************************************
|
|
2
|
+
* Copyright 2025 Adobe
|
|
3
|
+
* All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
|
+
*******************************************************************/
|
|
9
|
+
|
|
1
10
|
const path = require('path');
|
|
2
11
|
const { toPascalCase, toCamelCase } = require('../../lib/string');
|
|
3
12
|
const m = require('../../lib/log-message');
|
package/bin/lib/cli.js
CHANGED
|
@@ -4,5 +4,20 @@ module.exports = function cli(command) {
|
|
|
4
4
|
let cmd = command;
|
|
5
5
|
const argvs = process.argv.slice(3).join(' ');
|
|
6
6
|
if (argvs) cmd += ` ${argvs}`;
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
const child = spawn(cmd, { shell: true, stdio: 'inherit' });
|
|
10
|
+
|
|
11
|
+
child.on('close', (code) => {
|
|
12
|
+
if (code !== 0) {
|
|
13
|
+
reject(new Error(`Command failed with exit code ${code}`));
|
|
14
|
+
} else {
|
|
15
|
+
resolve(child);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
child.on('error', (err) => {
|
|
20
|
+
reject(err);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
8
23
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adobe-commerce/elsie",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0-alpha2345",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
5
5
|
"description": "Domain Package SDK",
|
|
6
6
|
"engines": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"dev": "concurrently 'yarn storybook' 'yarn serve'",
|
|
16
16
|
"storybook": "elsie storybook",
|
|
17
17
|
"serve": "elsie serve --config vite.config.mjs",
|
|
18
|
-
"lint": "elsie lint
|
|
18
|
+
"lint": "elsie lint",
|
|
19
19
|
"test": "elsie test",
|
|
20
20
|
"test:ci": "jest --config jest.config.js --passWithNoTests --coverage",
|
|
21
21
|
"build": "elsie build --config vite.config.mjs",
|
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
|
-
import { ComponentChildren, FunctionComponent, VNode } from 'preact';
|
|
10
|
+
import { ComponentChildren, FunctionComponent, VNode, JSX } from 'preact';
|
|
11
11
|
import { HTMLAttributes } from 'preact/compat';
|
|
12
12
|
import { classes, VComponent } from '@adobe-commerce/elsie/lib';
|
|
13
13
|
import { Divider } from '@adobe-commerce/elsie/components';
|
|
14
14
|
import '@adobe-commerce/elsie/components/Header/Header.css';
|
|
15
15
|
|
|
16
|
-
export interface HeaderProps extends HTMLAttributes<HTMLDivElement> {
|
|
16
|
+
export interface HeaderProps extends Omit<HTMLAttributes<HTMLDivElement>, 'size'> {
|
|
17
17
|
title: string;
|
|
18
18
|
size?: 'medium' | 'large';
|
|
19
19
|
divider?: boolean;
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
overflow: hidden;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
.dropin-image-swatch__span img,
|
|
36
37
|
.dropin-image-swatch__content {
|
|
37
38
|
width: inherit;
|
|
38
39
|
position: absolute;
|
|
@@ -121,10 +122,19 @@
|
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
.dropin-image-swatch__span--out-of-stock > .dropin-image-swatch__content,
|
|
125
|
+
.dropin-image-swatch__span--out-of-stock img,
|
|
126
|
+
.dropin-image-swatch__container
|
|
127
|
+
input[type='radio']:disabled
|
|
128
|
+
~ .dropin-image-swatch__span
|
|
129
|
+
img,
|
|
124
130
|
.dropin-image-swatch__container
|
|
125
131
|
input[type='radio']:disabled
|
|
126
132
|
~ .dropin-image-swatch__span
|
|
127
133
|
> .dropin-image-swatch__content,
|
|
134
|
+
.dropin-image-swatch__container
|
|
135
|
+
input[type='checkbox']:disabled
|
|
136
|
+
~ .dropin-image-swatch__span
|
|
137
|
+
img,
|
|
128
138
|
.dropin-image-swatch__container
|
|
129
139
|
input[type='checkbox']:disabled
|
|
130
140
|
~ .dropin-image-swatch__span
|
|
@@ -260,11 +260,11 @@ export const MultiImageSwatch: Story = {
|
|
|
260
260
|
),
|
|
261
261
|
};
|
|
262
262
|
|
|
263
|
-
export const
|
|
263
|
+
export const CustomImageNodeVNodeSwatch: Story = {
|
|
264
264
|
args: {
|
|
265
265
|
name: 'customImageSwatch',
|
|
266
266
|
id: 'customImageSwatch1',
|
|
267
|
-
label: 'Custom Image Node Example',
|
|
267
|
+
label: 'Custom Image Node VNode Example',
|
|
268
268
|
groupAriaLabel: 'Custom Image Swatches',
|
|
269
269
|
value: 'customImageNode',
|
|
270
270
|
src: `https://picsum.photos/${defaultWidth}/${defaultHeight}`, // fallback, not used with imageNode
|
|
@@ -277,7 +277,7 @@ export const CustomImageNodeSwatch: Story = {
|
|
|
277
277
|
<div style="position: relative; width: 100%; height: 100%;">
|
|
278
278
|
<img
|
|
279
279
|
src={`https://picsum.photos/${defaultWidth}/${defaultHeight}?grayscale`}
|
|
280
|
-
alt="Custom grayscale image"
|
|
280
|
+
alt="Custom grayscale image - VNode"
|
|
281
281
|
style="width: 100%; height: 100%; object-fit: cover;"
|
|
282
282
|
/>
|
|
283
283
|
<div style="position: absolute; top: 0; left: 0; background: rgba(255,255,255,0.7); padding: 4px 8px; border-radius: 0 0 8px 0;">
|
|
@@ -295,7 +295,56 @@ export const CustomImageNodeSwatch: Story = {
|
|
|
295
295
|
'div[style*="position: relative"]'
|
|
296
296
|
);
|
|
297
297
|
const customImage = canvasElement.querySelector(
|
|
298
|
-
'img[alt="Custom grayscale image"]'
|
|
298
|
+
'img[alt="Custom grayscale image - VNode"]'
|
|
299
|
+
);
|
|
300
|
+
const customLabel = canvasElement.querySelector(
|
|
301
|
+
'span[style*="font-weight: bold"]'
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
expect(imageSwatch).toBeInTheDocument();
|
|
305
|
+
expect(customImageContainer).toBeInTheDocument();
|
|
306
|
+
expect(customImage).toBeInTheDocument();
|
|
307
|
+
expect(customLabel).toBeInTheDocument();
|
|
308
|
+
expect(customLabel?.textContent).toBe('Custom');
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
export const CustomImageNodeRenderFunctionSwatch: Story = {
|
|
313
|
+
args: {
|
|
314
|
+
name: 'customImageSwatch',
|
|
315
|
+
id: 'customImageSwatch2',
|
|
316
|
+
label: 'Custom Image Node Render Function Example',
|
|
317
|
+
groupAriaLabel: 'Custom Image Swatches',
|
|
318
|
+
value: 'customImageNode',
|
|
319
|
+
src: `https://picsum.photos/${defaultWidth}/${defaultHeight}`, // fallback, not used with imageNode
|
|
320
|
+
alt: 'Custom Image Node',
|
|
321
|
+
selected: false,
|
|
322
|
+
disabled: false,
|
|
323
|
+
outOfStock: false,
|
|
324
|
+
onValue: action('onValue'),
|
|
325
|
+
imageNode: () => (
|
|
326
|
+
<div style="position: relative; width: 100%; height: 100%;">
|
|
327
|
+
<img
|
|
328
|
+
src={`https://picsum.photos/${defaultWidth}/${defaultHeight}?grayscale`}
|
|
329
|
+
alt="Custom grayscale image - Render Function"
|
|
330
|
+
style="width: 100%; height: 100%; object-fit: cover;"
|
|
331
|
+
/>
|
|
332
|
+
<div style="position: absolute; top: 0; left: 0; background: rgba(255,255,255,0.7); padding: 4px 8px; border-radius: 0 0 8px 0;">
|
|
333
|
+
<span style="font-size: 12px; font-weight: bold; color: #333;">
|
|
334
|
+
Custom
|
|
335
|
+
</span>
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
),
|
|
339
|
+
},
|
|
340
|
+
play: async ({ canvasElement }) => {
|
|
341
|
+
const canvas = within(canvasElement);
|
|
342
|
+
const imageSwatch = await canvas.findByRole('radio');
|
|
343
|
+
const customImageContainer = canvasElement.querySelector(
|
|
344
|
+
'div[style*="position: relative"]'
|
|
345
|
+
);
|
|
346
|
+
const customImage = canvasElement.querySelector(
|
|
347
|
+
'img[alt="Custom grayscale image - Render Function"]'
|
|
299
348
|
);
|
|
300
349
|
const customLabel = canvasElement.querySelector(
|
|
301
350
|
'span[style*="font-weight: bold"]'
|
|
@@ -8,11 +8,26 @@
|
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
10
|
import { FunctionComponent, VNode } from 'preact';
|
|
11
|
-
import { HTMLAttributes, useCallback } from 'preact/compat';
|
|
11
|
+
import { HTMLAttributes, useCallback, JSX, useMemo } from 'preact/compat';
|
|
12
12
|
import { classes } from '@adobe-commerce/elsie/lib';
|
|
13
13
|
import '@adobe-commerce/elsie/components/ImageSwatch/ImageSwatch.css';
|
|
14
|
-
import { Image } from '@adobe-commerce/elsie/components/Image';
|
|
14
|
+
import { Image, ImageProps } from '@adobe-commerce/elsie/components/Image';
|
|
15
15
|
import { useText } from '@adobe-commerce/elsie/i18n';
|
|
16
|
+
|
|
17
|
+
export interface ImageNodeRenderProps extends ImageProps {
|
|
18
|
+
imageSwatchContext: {
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
outOfStock?: boolean;
|
|
21
|
+
multi?: boolean;
|
|
22
|
+
selected?: boolean;
|
|
23
|
+
value?: string;
|
|
24
|
+
label?: string;
|
|
25
|
+
groupAriaLabel?: string;
|
|
26
|
+
name?: string;
|
|
27
|
+
id?: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
16
31
|
export interface ImageSwatchProps
|
|
17
32
|
extends Omit<HTMLAttributes<HTMLInputElement>, 'label'> {
|
|
18
33
|
name?: string;
|
|
@@ -26,7 +41,7 @@ export interface ImageSwatchProps
|
|
|
26
41
|
selected?: boolean;
|
|
27
42
|
outOfStock?: boolean;
|
|
28
43
|
multi?: boolean;
|
|
29
|
-
imageNode?: VNode;
|
|
44
|
+
imageNode?: VNode | ((props: ImageNodeRenderProps) => JSX.Element);
|
|
30
45
|
onValue?: (value: any) => void;
|
|
31
46
|
onUpdateError?: (error: Error) => void;
|
|
32
47
|
}
|
|
@@ -80,6 +95,16 @@ export const ImageSwatch: FunctionComponent<ImageSwatchProps> = ({
|
|
|
80
95
|
return `${groupAriaLabel}: ${label} ${swatchLabel}`;
|
|
81
96
|
};
|
|
82
97
|
|
|
98
|
+
const imageProps: ImageProps = useMemo(() => {
|
|
99
|
+
return {
|
|
100
|
+
src,
|
|
101
|
+
alt,
|
|
102
|
+
loading: 'lazy',
|
|
103
|
+
params: { width: 100, fit: 'bounds', crop: true },
|
|
104
|
+
onError: (e: any) => (e.target.style.display = 'none'),
|
|
105
|
+
};
|
|
106
|
+
}, [src, alt]);
|
|
107
|
+
|
|
83
108
|
return (
|
|
84
109
|
<label className={classes(['dropin-image-swatch__container', className])}>
|
|
85
110
|
<input
|
|
@@ -107,14 +132,24 @@ export const ImageSwatch: FunctionComponent<ImageSwatchProps> = ({
|
|
|
107
132
|
className,
|
|
108
133
|
])}
|
|
109
134
|
>
|
|
110
|
-
{imageNode
|
|
135
|
+
{typeof imageNode === 'function' ? (
|
|
136
|
+
imageNode({
|
|
137
|
+
...imageProps,
|
|
138
|
+
imageSwatchContext: {
|
|
139
|
+
disabled,
|
|
140
|
+
outOfStock,
|
|
141
|
+
selected,
|
|
142
|
+
value,
|
|
143
|
+
label,
|
|
144
|
+
groupAriaLabel,
|
|
145
|
+
name,
|
|
146
|
+
id,
|
|
147
|
+
},
|
|
148
|
+
})
|
|
149
|
+
) : imageNode || (
|
|
111
150
|
<Image
|
|
112
|
-
|
|
151
|
+
{...imageProps}
|
|
113
152
|
className={classes(['dropin-image-swatch__content'])}
|
|
114
|
-
params={{ width: 100, fit: 'bounds', crop: true }}
|
|
115
|
-
alt={alt}
|
|
116
|
-
loading={'lazy'}
|
|
117
|
-
onError={(e: any) => (e.target.style.display = 'none')}
|
|
118
153
|
/>
|
|
119
154
|
)}
|
|
120
155
|
</span>
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
10
|
import { FunctionComponent } from 'preact';
|
|
11
|
-
import { useState, useCallback } from 'preact/hooks';
|
|
11
|
+
import { useState, useCallback, useEffect } from 'preact/hooks';
|
|
12
12
|
import { HTMLAttributes } from 'preact/compat';
|
|
13
13
|
import { classes, debounce } from '@adobe-commerce/elsie/lib';
|
|
14
14
|
import { Add, Minus } from '@adobe-commerce/elsie/icons';
|
|
@@ -56,6 +56,15 @@ export const Incrementer: FunctionComponent<IncrementerProps> = ({
|
|
|
56
56
|
? 'Dropin.Incrementer.maxQuantityMessage'
|
|
57
57
|
: 'Dropin.Incrementer.errorMessage';
|
|
58
58
|
|
|
59
|
+
// Add this effect to synchronize internal state with external value prop
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
const propValue = Number(value);
|
|
62
|
+
if (propValue !== currentValue) {
|
|
63
|
+
setCurrentValue(propValue);
|
|
64
|
+
}
|
|
65
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
66
|
+
}, [value]);
|
|
67
|
+
|
|
59
68
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
60
69
|
const debouncedOnValueHandler = useCallback(
|
|
61
70
|
debounce(async (newValue: any) => {
|
|
@@ -48,6 +48,7 @@ const Template: StoryObj<TagProps> = {
|
|
|
48
48
|
<Tag {...args}>
|
|
49
49
|
{/* This workaround allows children to be edited as plain text in Storybook */}
|
|
50
50
|
{args.children && typeof args.children === 'string' ? (
|
|
51
|
+
// eslint-disable-next-line react/no-danger
|
|
51
52
|
<span dangerouslySetInnerHTML={{ __html: args.children }} />
|
|
52
53
|
) : undefined}
|
|
53
54
|
</Tag>
|
package/src/docs/slots.mdx
CHANGED
|
@@ -32,23 +32,41 @@ The `<Slot />` component is used to define a slot in a container. It receives a
|
|
|
32
32
|
|
|
33
33
|
The name of the slot in _PascalCase_. `string` (required).
|
|
34
34
|
|
|
35
|
-
###
|
|
35
|
+
### slotTag
|
|
36
36
|
|
|
37
37
|
The HTML tag to use for the slot's wrapper element. This allows you to change the wrapper element from the default `div` to any valid HTML tag (e.g., 'span', 'p', 'a', etc.). When using specific tags like 'a', you can also provide their respective HTML attributes (e.g., 'href', 'target', etc.).
|
|
38
38
|
|
|
39
39
|
Example:
|
|
40
40
|
```tsx
|
|
41
41
|
// Render with a span wrapper
|
|
42
|
-
<Slot name="MySlot"
|
|
42
|
+
<Slot name="MySlot" slotTag="span">
|
|
43
43
|
Inline content
|
|
44
44
|
</Slot>
|
|
45
45
|
|
|
46
46
|
// Render with an anchor wrapper
|
|
47
|
-
<Slot name="MySlot"
|
|
47
|
+
<Slot name="MySlot" slotTag="a" href="https://example.com" target="_blank">
|
|
48
48
|
Link content
|
|
49
49
|
</Slot>
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
### contentTag
|
|
53
|
+
|
|
54
|
+
The HTML tag to use for wrapping dynamically inserted content within the slot. This is separate from the slot's wrapper tag and allows you to control how dynamic content is structured. Defaults to 'div'.
|
|
55
|
+
|
|
56
|
+
Example:
|
|
57
|
+
```tsx
|
|
58
|
+
<Slot
|
|
59
|
+
name="MySlot"
|
|
60
|
+
slotTag="article" // The outer wrapper will be an article
|
|
61
|
+
contentTag="section" // Dynamic content will be wrapped in sections
|
|
62
|
+
slot={(ctx) => {
|
|
63
|
+
const elem = document.createElement('div');
|
|
64
|
+
elem.innerHTML = 'Dynamic content';
|
|
65
|
+
ctx.appendChild(elem); // This will be wrapped in a section tag
|
|
66
|
+
}}
|
|
67
|
+
/>
|
|
68
|
+
```
|
|
69
|
+
|
|
52
70
|
### slot (required)
|
|
53
71
|
|
|
54
72
|
- `ctx`: An object representing the context of the slot, including methods for manipulating the slot's content.
|
package/src/lib/slot.tsx
CHANGED
|
@@ -25,6 +25,7 @@ import '@adobe-commerce/elsie/components/UIProvider/debugger.css';
|
|
|
25
25
|
|
|
26
26
|
type MutateElement = (elem: HTMLElement) => void;
|
|
27
27
|
|
|
28
|
+
|
|
28
29
|
interface State {
|
|
29
30
|
get: (key: string) => void;
|
|
30
31
|
set: (key: string, value: any) => void;
|
|
@@ -42,7 +43,8 @@ interface PrivateContext<T> {
|
|
|
42
43
|
_registerMethod: (
|
|
43
44
|
cb: (next: T & DefaultSlotContext<T>, state: State) => void
|
|
44
45
|
) => void;
|
|
45
|
-
|
|
46
|
+
// eslint-disable-next-line no-undef
|
|
47
|
+
_htmlElementToVNode: (element: HTMLElement, tag: keyof HTMLElementTagNameMap) => VNode;
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
interface DefaultSlotContext<T> extends PrivateContext<T> {
|
|
@@ -75,7 +77,9 @@ export function useSlot<K, V extends HTMLElement>(
|
|
|
75
77
|
context: Context<K> = {},
|
|
76
78
|
callback?: SlotProps<K>,
|
|
77
79
|
children?: ComponentChildren,
|
|
78
|
-
render?: Function
|
|
80
|
+
render?: Function,
|
|
81
|
+
// eslint-disable-next-line no-undef
|
|
82
|
+
contentTag: keyof HTMLElementTagNameMap = 'div'
|
|
79
83
|
): [RefObject<V>, Record<string, any>] {
|
|
80
84
|
const slotsQueue = useContext(SlotQueueContext);
|
|
81
85
|
|
|
@@ -146,15 +150,17 @@ export function useSlot<K, V extends HTMLElement>(
|
|
|
146
150
|
context._registerMethod = _registerMethod;
|
|
147
151
|
|
|
148
152
|
const _htmlElementToVNode = useCallback((elem: HTMLElement) => {
|
|
149
|
-
return (
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
+
return createElement(
|
|
154
|
+
contentTag,
|
|
155
|
+
{
|
|
156
|
+
'data-slot-html-element': elem.tagName.toLowerCase(),
|
|
157
|
+
ref: (refElem: HTMLElement | null): void => {
|
|
153
158
|
refElem?.appendChild(elem);
|
|
154
|
-
}
|
|
155
|
-
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
null
|
|
156
162
|
);
|
|
157
|
-
}, []);
|
|
163
|
+
}, [contentTag]);
|
|
158
164
|
|
|
159
165
|
// @ts-ignore
|
|
160
166
|
context._htmlElementToVNode = _htmlElementToVNode;
|
|
@@ -316,7 +322,7 @@ export function useSlot<K, V extends HTMLElement>(
|
|
|
316
322
|
status.current = 'loading';
|
|
317
323
|
|
|
318
324
|
log(`🟩 "${name}" Slot Initialized`);
|
|
319
|
-
await callback(context as K & DefaultSlotContext<K>, elementRef.current);
|
|
325
|
+
await callback(context as K & DefaultSlotContext<K>, elementRef.current as HTMLDivElement | null);
|
|
320
326
|
} catch (error) {
|
|
321
327
|
console.error(`Error in "${callback.name}" Slot callback`, error);
|
|
322
328
|
} finally {
|
|
@@ -356,7 +362,10 @@ interface SlotPropsComponent<T>
|
|
|
356
362
|
slot?: SlotProps<T>;
|
|
357
363
|
context?: Context<T>;
|
|
358
364
|
render?: (props: Record<string, any>) => VNode | VNode[];
|
|
359
|
-
|
|
365
|
+
// eslint-disable-next-line no-undef
|
|
366
|
+
slotTag?: keyof HTMLElementTagNameMap; // The tag for the slot wrapper itself
|
|
367
|
+
// eslint-disable-next-line no-undef
|
|
368
|
+
contentTag?: keyof HTMLElementTagNameMap; // The tag for dynamically inserted content
|
|
360
369
|
children?: ComponentChildren;
|
|
361
370
|
}
|
|
362
371
|
|
|
@@ -366,9 +375,14 @@ export function Slot<T>({
|
|
|
366
375
|
slot,
|
|
367
376
|
children,
|
|
368
377
|
render,
|
|
369
|
-
|
|
378
|
+
slotTag = 'div',
|
|
379
|
+
contentTag = 'div',
|
|
370
380
|
...props
|
|
371
|
-
}: Readonly<SlotPropsComponent<T>>) {
|
|
381
|
+
}: Readonly<SlotPropsComponent<T>>): VNode<{
|
|
382
|
+
ref: RefObject<HTMLElement>;
|
|
383
|
+
'data-slot': string;
|
|
384
|
+
[key: string]: any;
|
|
385
|
+
}> {
|
|
372
386
|
const slotsQueue = useContext(SlotQueueContext);
|
|
373
387
|
|
|
374
388
|
const [elementRef, slotProps] = useSlot<T, HTMLElement>(
|
|
@@ -376,7 +390,8 @@ export function Slot<T>({
|
|
|
376
390
|
context,
|
|
377
391
|
slot,
|
|
378
392
|
children,
|
|
379
|
-
render
|
|
393
|
+
render,
|
|
394
|
+
contentTag
|
|
380
395
|
);
|
|
381
396
|
|
|
382
397
|
useMemo(() => {
|
|
@@ -392,7 +407,7 @@ export function Slot<T>({
|
|
|
392
407
|
}, [name, slotsQueue]);
|
|
393
408
|
|
|
394
409
|
return createElement(
|
|
395
|
-
|
|
410
|
+
slotTag,
|
|
396
411
|
{
|
|
397
412
|
...props,
|
|
398
413
|
ref: elementRef,
|