@momentum-design/components 0.0.1

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.
Files changed (235) hide show
  1. package/.eslintrc.js +16 -0
  2. package/CONTRIBUTING.md +5 -0
  3. package/README.md +39 -0
  4. package/SCRIPTS.md +15 -0
  5. package/TESTING.md +38 -0
  6. package/config/api-extractor.json +33 -0
  7. package/config/custom-elements-manifest.config.js +28 -0
  8. package/config/esbuild/configs/browser.js +21 -0
  9. package/config/esbuild/configs/e2e.js +10 -0
  10. package/config/esbuild/esbuild-e2e.config.js +22 -0
  11. package/config/esbuild/esbuild.config.js +9 -0
  12. package/config/playwright/playwright.config.ts +107 -0
  13. package/config/playwright/public/index.css +43 -0
  14. package/config/playwright/public/index.html +26 -0
  15. package/config/playwright/setup/Components.page.ts +163 -0
  16. package/config/playwright/setup/constants.ts +27 -0
  17. package/config/playwright/setup/index.ts +42 -0
  18. package/config/playwright/setup/steps/accessibility.ts +9 -0
  19. package/config/playwright/setup/types.ts +5 -0
  20. package/config/playwright/setup/utils/accessibility.ts +70 -0
  21. package/config/playwright/setup/utils/visual-regression.ts +35 -0
  22. package/config/plop/actions/AddComponent.ts +21 -0
  23. package/config/plop/actions/AddToComponentExports.ts +16 -0
  24. package/config/plop/actions/AddToComponentImports.ts +16 -0
  25. package/config/plop/constants/index.ts +31 -0
  26. package/config/plop/esbuild.config.plop.js +4 -0
  27. package/config/plop/generators/component/index.ts +25 -0
  28. package/config/plop/plopfile.ts +6 -0
  29. package/config/plop/prompts/index.ts +8 -0
  30. package/config/plop/templates/add/component/index.ts.hbs +12 -0
  31. package/config/plop/templates/add/component/{{componentName}}.component.ts.hbs +22 -0
  32. package/config/plop/templates/add/component/{{componentName}}.constants.ts.hbs +5 -0
  33. package/config/plop/templates/add/component/{{componentName}}.e2e-test.ts.hbs +67 -0
  34. package/config/plop/templates/add/component/{{componentName}}.fixtures.ts.hbs +13 -0
  35. package/config/plop/templates/add/component/{{componentName}}.stories.ts.hbs +18 -0
  36. package/config/plop/templates/add/component/{{componentName}}.styles.ts.hbs +8 -0
  37. package/config/plop/tsconfig.plop.json +11 -0
  38. package/config/storybook/MomentumStorybookTheme.js +41 -0
  39. package/config/storybook/main.js +21 -0
  40. package/config/storybook/manager.js +17 -0
  41. package/config/storybook/preview.js +63 -0
  42. package/config/storybook/provider/iconProvider.js +8 -0
  43. package/config/storybook/provider/themeProvider.js +31 -0
  44. package/config/storybook/public/background-graphic.png +0 -0
  45. package/config/storybook/public/fonts/Inter.var.woff2 +0 -0
  46. package/config/storybook/public/momentum-logo.png +0 -0
  47. package/config/storybook/themes/index.js +14 -0
  48. package/config/storybook/themes/themes.css +15 -0
  49. package/data/custom-elements.json +677 -0
  50. package/dist/browser/index.js +366 -0
  51. package/dist/browser/index.js.map +7 -0
  52. package/dist/components/avatar/avatar.component.d.ts +28 -0
  53. package/dist/components/avatar/avatar.component.js +79 -0
  54. package/dist/components/avatar/avatar.constants.d.ts +7 -0
  55. package/dist/components/avatar/avatar.constants.js +14 -0
  56. package/dist/components/avatar/avatar.styles.d.ts +2 -0
  57. package/dist/components/avatar/avatar.styles.js +20 -0
  58. package/dist/components/avatar/avatar.types.d.ts +1 -0
  59. package/dist/components/avatar/avatar.types.js +2 -0
  60. package/dist/components/avatar/index.d.ts +7 -0
  61. package/dist/components/avatar/index.js +7 -0
  62. package/dist/components/badge/badge.component.d.ts +51 -0
  63. package/dist/components/badge/badge.component.js +114 -0
  64. package/dist/components/badge/badge.constants.d.ts +8 -0
  65. package/dist/components/badge/badge.constants.js +15 -0
  66. package/dist/components/badge/badge.styles.d.ts +2 -0
  67. package/dist/components/badge/badge.styles.js +26 -0
  68. package/dist/components/badge/badge.types.d.ts +1 -0
  69. package/dist/components/badge/badge.types.js +2 -0
  70. package/dist/components/badge/index.d.ts +7 -0
  71. package/dist/components/badge/index.js +7 -0
  72. package/dist/components/icon/icon.component.d.ts +63 -0
  73. package/dist/components/icon/icon.component.js +158 -0
  74. package/dist/components/icon/icon.constants.d.ts +6 -0
  75. package/dist/components/icon/icon.constants.js +12 -0
  76. package/dist/components/icon/icon.styles.d.ts +2 -0
  77. package/dist/components/icon/icon.styles.js +15 -0
  78. package/dist/components/icon/icon.utils.d.ts +2 -0
  79. package/dist/components/icon/icon.utils.js +13 -0
  80. package/dist/components/icon/index.d.ts +7 -0
  81. package/dist/components/icon/index.js +7 -0
  82. package/dist/components/iconprovider/iconprovider.component.d.ts +34 -0
  83. package/dist/components/iconprovider/iconprovider.component.js +71 -0
  84. package/dist/components/iconprovider/iconprovider.constants.d.ts +7 -0
  85. package/dist/components/iconprovider/iconprovider.constants.js +14 -0
  86. package/dist/components/iconprovider/iconprovider.context.d.ts +9 -0
  87. package/dist/components/iconprovider/iconprovider.context.js +9 -0
  88. package/dist/components/iconprovider/index.d.ts +7 -0
  89. package/dist/components/iconprovider/index.js +7 -0
  90. package/dist/components/text/fonts.styles.d.ts +1 -0
  91. package/dist/components/text/fonts.styles.js +100 -0
  92. package/dist/components/text/index.d.ts +7 -0
  93. package/dist/components/text/index.js +7 -0
  94. package/dist/components/text/text.component.d.ts +29 -0
  95. package/dist/components/text/text.component.js +41 -0
  96. package/dist/components/text/text.constants.d.ts +9 -0
  97. package/dist/components/text/text.constants.js +28 -0
  98. package/dist/components/text/text.styles.d.ts +2 -0
  99. package/dist/components/text/text.styles.js +17 -0
  100. package/dist/components/text/text.types.d.ts +1 -0
  101. package/dist/components/text/text.types.js +2 -0
  102. package/dist/components/text/text.utils.d.ts +20 -0
  103. package/dist/components/text/text.utils.js +50 -0
  104. package/dist/components/themeprovider/index.d.ts +7 -0
  105. package/dist/components/themeprovider/index.js +7 -0
  106. package/dist/components/themeprovider/themeprovider.component.d.ts +48 -0
  107. package/dist/components/themeprovider/themeprovider.component.js +86 -0
  108. package/dist/components/themeprovider/themeprovider.constants.d.ts +10 -0
  109. package/dist/components/themeprovider/themeprovider.constants.js +31 -0
  110. package/dist/components/themeprovider/themeprovider.context.d.ts +9 -0
  111. package/dist/components/themeprovider/themeprovider.context.js +13 -0
  112. package/dist/components/themeprovider/themeprovider.styles.d.ts +2 -0
  113. package/dist/components/themeprovider/themeprovider.styles.js +13 -0
  114. package/dist/components/themeprovider/themeprovider.types.d.ts +5 -0
  115. package/dist/components/themeprovider/themeprovider.types.js +2 -0
  116. package/dist/components/themeprovider/themeprovider.utils.d.ts +9 -0
  117. package/dist/components/themeprovider/themeprovider.utils.js +10 -0
  118. package/dist/index.d.ts +8 -0
  119. package/dist/index.js +19 -0
  120. package/dist/models/component/component.component.d.ts +38 -0
  121. package/dist/models/component/component.component.js +45 -0
  122. package/dist/models/component/component.types.d.ts +15 -0
  123. package/dist/models/component/component.types.js +2 -0
  124. package/dist/models/component/index.d.ts +3 -0
  125. package/dist/models/component/index.js +5 -0
  126. package/dist/models/index.d.ts +4 -0
  127. package/dist/models/index.js +8 -0
  128. package/dist/models/provider/index.d.ts +2 -0
  129. package/dist/models/provider/index.js +5 -0
  130. package/dist/models/provider/provider.component.d.ts +70 -0
  131. package/dist/models/provider/provider.component.js +56 -0
  132. package/dist/models/provider/provider.styles.d.ts +2 -0
  133. package/dist/models/provider/provider.styles.js +14 -0
  134. package/dist/utils/provider/index.d.ts +13 -0
  135. package/dist/utils/provider/index.js +14 -0
  136. package/dist/utils/styles/index.d.ts +2 -0
  137. package/dist/utils/styles/index.js +14 -0
  138. package/dist/utils/tag-name/constants.d.ts +7 -0
  139. package/dist/utils/tag-name/constants.js +10 -0
  140. package/dist/utils/tag-name/index.d.ts +4 -0
  141. package/dist/utils/tag-name/index.js +10 -0
  142. package/dist/utils/types.d.ts +1 -0
  143. package/dist/utils/types.js +2 -0
  144. package/jest.config.js +3 -0
  145. package/package.json +78 -0
  146. package/scripts/copyFonts.js +31 -0
  147. package/scripts/copyIcons.js +31 -0
  148. package/scripts/copyTokens.js +24 -0
  149. package/src/components/avatar/__screenshots__/mdc-avatar.png +0 -0
  150. package/src/components/avatar/avatar.component.ts +74 -0
  151. package/src/components/avatar/avatar.constants.ts +12 -0
  152. package/src/components/avatar/avatar.e2e-test.ts +70 -0
  153. package/src/components/avatar/avatar.stories.ts +25 -0
  154. package/src/components/avatar/avatar.styles.ts +20 -0
  155. package/src/components/avatar/avatar.types.ts +1 -0
  156. package/src/components/avatar/index.ts +12 -0
  157. package/src/components/badge/__screenshots__/mdc-badge.png +0 -0
  158. package/src/components/badge/badge.component.ts +121 -0
  159. package/src/components/badge/badge.constants.ts +13 -0
  160. package/src/components/badge/badge.e2e-test.ts +68 -0
  161. package/src/components/badge/badge.stories.ts +33 -0
  162. package/src/components/badge/badge.styles.ts +26 -0
  163. package/src/components/badge/badge.types.ts +1 -0
  164. package/src/components/badge/index.ts +12 -0
  165. package/src/components/icon/__screenshots__/mdc-icon-default.png +0 -0
  166. package/src/components/icon/__screenshots__/mdc-icon-scale.png +0 -0
  167. package/src/components/icon/icon.component.ts +155 -0
  168. package/src/components/icon/icon.constants.ts +10 -0
  169. package/src/components/icon/icon.e2e-test.ts +101 -0
  170. package/src/components/icon/icon.stories.ts +34 -0
  171. package/src/components/icon/icon.styles.ts +15 -0
  172. package/src/components/icon/icon.utils.ts +13 -0
  173. package/src/components/icon/index.ts +12 -0
  174. package/src/components/iconprovider/__screenshots__/mdc-iconprovider.png +0 -0
  175. package/src/components/iconprovider/iconprovider.component.ts +76 -0
  176. package/src/components/iconprovider/iconprovider.constants.ts +12 -0
  177. package/src/components/iconprovider/iconprovider.context.ts +16 -0
  178. package/src/components/iconprovider/iconprovider.e2e-test.ts +65 -0
  179. package/src/components/iconprovider/iconprovider.stories.ts +27 -0
  180. package/src/components/iconprovider/iconprovider.stories.utils.ts +27 -0
  181. package/src/components/iconprovider/index.ts +12 -0
  182. package/src/components/text/__screenshots__/mdc-text-body-large.png +0 -0
  183. package/src/components/text/__screenshots__/mdc-text-body-regular.png +0 -0
  184. package/src/components/text/__screenshots__/mdc-text-body-small.png +0 -0
  185. package/src/components/text/__screenshots__/mdc-text-heading-1.png +0 -0
  186. package/src/components/text/__screenshots__/mdc-text-heading-2.png +0 -0
  187. package/src/components/text/__screenshots__/mdc-text-heading-3.png +0 -0
  188. package/src/components/text/__screenshots__/mdc-text-heading-4.png +0 -0
  189. package/src/components/text/__screenshots__/mdc-text-heading-5.png +0 -0
  190. package/src/components/text/__screenshots__/mdc-text-heading-6.png +0 -0
  191. package/src/components/text/__screenshots__/mdc-text-heading-7.png +0 -0
  192. package/src/components/text/__screenshots__/mdc-text-image-title.png +0 -0
  193. package/src/components/text/__screenshots__/mdc-text-label.png +0 -0
  194. package/src/components/text/fonts.styles.ts +99 -0
  195. package/src/components/text/index.ts +12 -0
  196. package/src/components/text/text.component.ts +51 -0
  197. package/src/components/text/text.constants.ts +27 -0
  198. package/src/components/text/text.e2e-test.ts +76 -0
  199. package/src/components/text/text.stories.ts +29 -0
  200. package/src/components/text/text.styles.ts +17 -0
  201. package/src/components/text/text.types.ts +13 -0
  202. package/src/components/text/text.utils.ts +51 -0
  203. package/src/components/themeprovider/__screenshots__/mdc-themeprovider-darkWebex.png +0 -0
  204. package/src/components/themeprovider/__screenshots__/mdc-themeprovider-lightWebex.png +0 -0
  205. package/src/components/themeprovider/index.ts +12 -0
  206. package/src/components/themeprovider/themeprovider.component.ts +91 -0
  207. package/src/components/themeprovider/themeprovider.constants.ts +32 -0
  208. package/src/components/themeprovider/themeprovider.context.ts +18 -0
  209. package/src/components/themeprovider/themeprovider.e2e-test.ts +89 -0
  210. package/src/components/themeprovider/themeprovider.stories.styles.css +22 -0
  211. package/src/components/themeprovider/themeprovider.stories.ts +38 -0
  212. package/src/components/themeprovider/themeprovider.stories.utils.ts +23 -0
  213. package/src/components/themeprovider/themeprovider.styles.ts +13 -0
  214. package/src/components/themeprovider/themeprovider.types.ts +7 -0
  215. package/src/components/themeprovider/themeprovider.utils.ts +16 -0
  216. package/src/index.ts +22 -0
  217. package/src/models/component/component.component.ts +46 -0
  218. package/src/models/component/component.types.ts +16 -0
  219. package/src/models/component/index.ts +7 -0
  220. package/src/models/index.ts +11 -0
  221. package/src/models/provider/index.ts +3 -0
  222. package/src/models/provider/provider.component.ts +87 -0
  223. package/src/models/provider/provider.styles.ts +14 -0
  224. package/src/stories/colors.mdx +32 -0
  225. package/src/stories/icons.mdx +13 -0
  226. package/src/stories/typography.mdx +20 -0
  227. package/src/utils/mixins/DisabledMixin.ts +19 -0
  228. package/src/utils/mixins/TabIndexMixin.ts +19 -0
  229. package/src/utils/provider/index.ts +21 -0
  230. package/src/utils/styles/index.ts +13 -0
  231. package/src/utils/tag-name/constants.ts +10 -0
  232. package/src/utils/tag-name/index.ts +15 -0
  233. package/src/utils/types.ts +1 -0
  234. package/tsconfig.json +45 -0
  235. package/tsconfig.module.json +47 -0
@@ -0,0 +1,99 @@
1
+ import { css } from 'lit';
2
+
3
+ // todo: change definition to tokens once available
4
+
5
+ export const fontsStyles = css`
6
+ :host([type="heading-1"]) {
7
+ font-size: 7.5rem;
8
+ font-style: normal;
9
+ font-weight: 700;
10
+ line-height: 125%; /* 9.375rem */
11
+ text-transform: capitalize;
12
+ }
13
+
14
+ :host([type="heading-2"]) {
15
+ font-size: 6rem;
16
+ font-style: normal;
17
+ font-weight: 700;
18
+ line-height: 125%; /* 7.5rem */
19
+ text-transform: capitalize;
20
+ }
21
+
22
+ :host([type="heading-3"]) {
23
+ font-size: 3rem;
24
+ font-style: normal;
25
+ font-weight: 700;
26
+ line-height: 125%; /* 3.75rem */
27
+ }
28
+
29
+ :host([type="heading-4"]) {
30
+ font-size: 2.5rem;
31
+ font-style: normal;
32
+ font-weight: 700;
33
+ line-height: 125%; /* 3.125rem */
34
+ }
35
+
36
+ :host([type="heading-5"]) {
37
+ font-size: 2rem;
38
+ font-style: normal;
39
+ font-weight: 700;
40
+ line-height: 125%; /* 2.5rem */
41
+ letter-spacing: 0.5rem;
42
+ text-transform: uppercase;
43
+ }
44
+
45
+ :host([type="heading-6"]) {
46
+ font-size: 1.75rem;
47
+ font-style: normal;
48
+ font-weight: 700;
49
+ line-height: 3rem; /* 171.429% */
50
+ }
51
+
52
+ :host([type="heading-7"]) {
53
+ font-size: 1.5rem;
54
+ font-style: normal;
55
+ font-weight: 700;
56
+ line-height: 140%; /* 2.1rem */
57
+ }
58
+
59
+ :host([type="body-large"]) {
60
+ font-size: 2rem;
61
+ font-style: normal;
62
+ font-weight: 400;
63
+ line-height: 150%; /* 3rem */
64
+ margin-block-end: 1.875em; /* paragraph spacing, 30px */
65
+ }
66
+
67
+ :host([type="body-regular"]) {
68
+ font-size: 1.5rem;
69
+ font-style: normal;
70
+ font-weight: 400;
71
+ line-height: 150%; /* 2.25rem */
72
+ margin-block-end: 1.5em; /* paragraph spacing, 24px */
73
+ }
74
+
75
+ :host([type="body-small"]) {
76
+ font-size: 1.25rem;
77
+ font-style: normal;
78
+ font-weight: 400;
79
+ line-height: 150%; /* 1.875rem */
80
+ margin-block-end: 0.125rem; /* paragraph spacing, 2px */
81
+ }
82
+
83
+ :host([type="image-title"]) {
84
+ font-size: 1rem;
85
+ font-style: normal;
86
+ font-weight: 700;
87
+ line-height: 150%; /* 1.5rem */
88
+ letter-spacing: 0.04rem;
89
+ text-transform: uppercase;
90
+ }
91
+
92
+ :host([type="label"]) {
93
+ font-size: 1rem;
94
+ font-style: normal;
95
+ font-weight: 700;
96
+ line-height: 150%; /* 1.5rem */
97
+ letter-spacing: 0.02rem;
98
+ }
99
+ `;
@@ -0,0 +1,12 @@
1
+ import Text from './text.component';
2
+ import { TAG_NAME } from './text.constants';
3
+
4
+ Text.register(TAG_NAME);
5
+
6
+ declare global {
7
+ interface HTMLElementTagNameMap {
8
+ ['mdc-text']: Text
9
+ }
10
+ }
11
+
12
+ export default Text;
@@ -0,0 +1,51 @@
1
+ import { html } from 'lit';
2
+ import { property } from 'lit/decorators.js';
3
+ import styles from './text.styles';
4
+ import { Component } from '../../models';
5
+ import type { FontType } from './text.types';
6
+ import { getRole, getAriaLevel } from './text.utils';
7
+
8
+ /**
9
+ * Text component, which helps creating a text element aligning with
10
+ * styling.
11
+ *
12
+ * The `type` attribute allows changing the type of text, like `heading-1`, etc.
13
+ *
14
+ * For accessibility the `role` and `aria-level` on the component are going to be set
15
+ * automatically based on the type e.g. heading-1 will lead to `role="heading"` and `aria-level=1`.
16
+ *
17
+ * @tag mdc-text
18
+ * @tagname mdc-text
19
+ */
20
+ class Text extends Component {
21
+ /**
22
+ * Text Type - defines how the text should be rendered
23
+ *
24
+ * Possible values: `heading-1` | `heading-2` | `heading-3` | `heading-4`
25
+ | `heading-5` | `heading-6` | `heading-7` | `body-large` | `body-regular`
26
+ | `body-small` | `image-title` | `label`;
27
+ */
28
+ @property({ attribute: 'type', reflect: true, type: String })
29
+ public type?: FontType;
30
+
31
+ protected handleTypeChanged(): void {
32
+ this.role = getRole(this.type);
33
+ this.ariaLevel = getAriaLevel(this.type);
34
+ }
35
+
36
+ protected override updated(changedProperties: Map<string, any>): void {
37
+ super.updated(changedProperties);
38
+
39
+ if (changedProperties.has('type')) {
40
+ this.handleTypeChanged();
41
+ }
42
+ }
43
+
44
+ public override render() {
45
+ return html`<slot></slot>`;
46
+ }
47
+
48
+ public static override styles = styles;
49
+ }
50
+
51
+ export default Text;
@@ -0,0 +1,27 @@
1
+ import utils from '../../utils/tag-name';
2
+ import type { FontType } from './text.types';
3
+
4
+ const TAG_NAME = utils.constructTagName('text');
5
+
6
+ const DEFAULTS = {
7
+ TYPE: 'body-regular' as const,
8
+ };
9
+
10
+ const VALUES: {TYPE: Array<FontType>} = {
11
+ TYPE: [
12
+ 'heading-1',
13
+ 'heading-2',
14
+ 'heading-3',
15
+ 'heading-4',
16
+ 'heading-5',
17
+ 'heading-6',
18
+ 'heading-7',
19
+ 'body-large',
20
+ 'body-regular',
21
+ 'body-small',
22
+ 'image-title',
23
+ 'label',
24
+ ],
25
+ };
26
+
27
+ export { TAG_NAME, DEFAULTS, VALUES };
@@ -0,0 +1,76 @@
1
+ /* eslint-disable no-restricted-syntax */
2
+ import { expect } from '@playwright/test';
3
+ import { ComponentsPage, test } from '../../../config/playwright/setup';
4
+ import steps from '../../../config/playwright/setup/steps/accessibility';
5
+ import { VALUES } from './text.constants';
6
+ import { FontType } from './text.types';
7
+ import { getAriaLevel, isHeading } from './text.utils';
8
+
9
+ type SetupOptions = {
10
+ componentsPage: ComponentsPage;
11
+ type: FontType;
12
+ children: any;
13
+ };
14
+
15
+ const setup = async (args: SetupOptions) => {
16
+ const { componentsPage, ...restArgs } = args;
17
+ await componentsPage.mount({
18
+ html: `
19
+ <mdc-text type="${restArgs.type}">${restArgs.children}</mdc-text>
20
+ `,
21
+ clearDocument: true,
22
+ });
23
+ const text = componentsPage.page.locator('mdc-text');
24
+ await text.waitFor();
25
+ return text;
26
+ };
27
+
28
+ const typesToTest: Array<FontType> = VALUES.TYPE;
29
+
30
+ test.describe('mdc-text', () => {
31
+ test.use({
32
+ viewport: {
33
+ width: 3000,
34
+ height: 500,
35
+ },
36
+ });
37
+ for (const textType of typesToTest) {
38
+ test(textType, async ({ componentsPage }) => {
39
+ const textContent = 'abcdefghijklmnopqrstuvwxyz1234567890';
40
+ const text = await setup({ componentsPage, type: textType, children: textContent });
41
+
42
+ /**
43
+ * ACCESSIBILITY
44
+ */
45
+ await test.step('accessibility', async () => {
46
+ await steps.automaticA11yCheckStep(componentsPage);
47
+ });
48
+
49
+ /**
50
+ * VISUAL REGRESSION
51
+ */
52
+ // TODO: fix visual regression test on CI
53
+ // await test.step('visual-regression', async () => {
54
+ // await test.step('matches screenshot of element', async () => {
55
+ // await componentsPage.visualRegression.takeScreenshot(`mdc-text-${textType}`, { element: text });
56
+ // });
57
+ // });
58
+
59
+ /**
60
+ * ATTRIBUTES
61
+ */
62
+ await test.step('attributes', async () => {
63
+ if (isHeading(textType)) {
64
+ await test.step('attribute role=heading should be present on component if type is heading', async () => {
65
+ expect(await text.getAttribute('role')).toBe('heading');
66
+ });
67
+
68
+ await test.step('attribute aria-level should be present on component if type is heading', async () => {
69
+ const expectedLevel = getAriaLevel(textType);
70
+ expect(await text.getAttribute('aria-level')).toBe(expectedLevel);
71
+ });
72
+ }
73
+ });
74
+ });
75
+ }
76
+ });
@@ -0,0 +1,29 @@
1
+ import type { Meta, StoryObj, Args } from '@storybook/web-components';
2
+ import '.';
3
+ import { html } from 'lit';
4
+ import { VALUES } from './text.constants';
5
+
6
+ const render = (args: Args) => html`
7
+ <mdc-text type="${args.type}">${args.children}</mdc-text>
8
+ `;
9
+
10
+ const meta: Meta = {
11
+ tags: ['autodocs'],
12
+ component: 'mdc-text',
13
+ render,
14
+ argTypes: {
15
+ type: {
16
+ control: 'radio',
17
+ options: VALUES.TYPE,
18
+ },
19
+ },
20
+ };
21
+
22
+ export default meta;
23
+
24
+ export const Primary: StoryObj = {
25
+ args: {
26
+ type: 'heading-1',
27
+ children: 'This is a test text',
28
+ },
29
+ };
@@ -0,0 +1,17 @@
1
+ import { css } from 'lit';
2
+ import { fontsStyles } from './fonts.styles';
3
+
4
+ const styles = [
5
+ css`
6
+ :host {
7
+ --mdc-text-font-family: var(--mdc-themeprovider-font-family);
8
+
9
+ display: block;
10
+ font-family: var(--mdc-text-font-family);
11
+ }
12
+ `,
13
+ // type specific font styles:
14
+ fontsStyles,
15
+ ];
16
+
17
+ export default styles;
@@ -0,0 +1,13 @@
1
+ export type FontType =
2
+ | 'heading-1'
3
+ | 'heading-2'
4
+ | 'heading-3'
5
+ | 'heading-4'
6
+ | 'heading-5'
7
+ | 'heading-6'
8
+ | 'heading-7'
9
+ | 'body-large'
10
+ | 'body-regular'
11
+ | 'body-small'
12
+ | 'image-title'
13
+ | 'label';
@@ -0,0 +1,51 @@
1
+ import { FontType } from './text.types';
2
+
3
+ /**
4
+ * Split the passed in type and return the parts
5
+ */
6
+ const getTypeParts = (type: FontType) => {
7
+ const parts = type.split('-');
8
+ return { firstPart: parts[0], secondPart: parts[1] };
9
+ };
10
+
11
+ /**
12
+ * Check if passed in type is a heading
13
+ * @param type - type to check
14
+ * @returns boolean, true if it is a heading
15
+ */
16
+ const isHeading = (type: FontType) => {
17
+ const { firstPart, secondPart } = getTypeParts(type);
18
+ return firstPart === 'heading' && +secondPart > 0 && +secondPart < 7;
19
+ };
20
+
21
+ /**
22
+ * Get corresponding aria-role to type
23
+ * @param type - type to find corresponding role for
24
+ * @returns role
25
+ */
26
+ const getRole = (type?: FontType) => {
27
+ if (type) {
28
+ if (isHeading(type)) {
29
+ return 'heading';
30
+ }
31
+ return 'paragraph';
32
+ }
33
+ return null;
34
+ };
35
+
36
+ /**
37
+ * Get corresponding aria-level to type
38
+ * @param type type to find corresponding level for
39
+ * @returns aria-level (has to be a number)
40
+ */
41
+ const getAriaLevel = (type?: FontType) => {
42
+ if (type) {
43
+ if (isHeading(type)) {
44
+ const { secondPart } = getTypeParts(type);
45
+ return secondPart;
46
+ }
47
+ }
48
+ return null;
49
+ };
50
+
51
+ export { isHeading, getAriaLevel, getRole };
@@ -0,0 +1,12 @@
1
+ import ThemeProvider from './themeprovider.component';
2
+ import { TAG_NAME } from './themeprovider.constants';
3
+
4
+ ThemeProvider.register(TAG_NAME);
5
+
6
+ declare global {
7
+ interface HTMLElementTagNameMap {
8
+ ['mdc-themeprovider']: ThemeProvider
9
+ }
10
+ }
11
+
12
+ export default ThemeProvider;
@@ -0,0 +1,91 @@
1
+ import { property } from 'lit/decorators.js';
2
+ import { DEFAULTS, THEMES } from './themeprovider.constants';
3
+ import { Provider } from '../../models';
4
+ import ThemeProviderContext from './themeprovider.context';
5
+ import styles from './themeprovider.styles';
6
+ import type { Theme } from './themeprovider.types';
7
+
8
+ /**
9
+ * ThemeProvider component, which sets the theme css variables
10
+ * for the child dom nodes and allows to be consumed from sub components
11
+ * (see providerUtils.consume for how to consume)
12
+ *
13
+ * ThemeProvider also includes the different font faces available
14
+ * for Text components.
15
+ *
16
+ * @tag mdc-themeprovider
17
+ * @tagname mdc-themeprovider
18
+ */
19
+ class ThemeProvider extends Provider<ThemeProviderContext> {
20
+ constructor() {
21
+ // initialise the context by running the Provider constructor:
22
+ super({
23
+ context: ThemeProviderContext.context,
24
+ initialValue: new ThemeProviderContext(DEFAULTS.THEME),
25
+ });
26
+ }
27
+
28
+ public static get Context() {
29
+ return ThemeProviderContext.context;
30
+ }
31
+
32
+ /**
33
+ * Available themes to switch to
34
+ *
35
+ * Has to be a space separated string, like className
36
+ * e.g.: `mds-theme-stable-darkWebex mds-theme-stable-lightWebex`
37
+ */
38
+ @property({ type: String })
39
+ themes: string = THEMES.join(' ');
40
+
41
+ /**
42
+ * Current theme attribute
43
+ *
44
+ * Has to be fully qualified, such that
45
+ * the theme name matches the className of the respective
46
+ * theme stylesheet
47
+ */
48
+ @property({ type: String })
49
+ theme: Theme = DEFAULTS.THEME;
50
+
51
+ protected override updated(changedProperties: Map<string, any>) {
52
+ super.updated(changedProperties);
53
+
54
+ if (changedProperties.has('theme')) {
55
+ this.updateActiveThemeClass();
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Update all observing components of this
61
+ * provider to update the theme
62
+ */
63
+ protected updateContext(): void {
64
+ let shouldUpdateConsumers = false;
65
+
66
+ if (this.context.value.theme !== this.theme) {
67
+ this.context.value.theme = this.theme;
68
+
69
+ shouldUpdateConsumers = true;
70
+ }
71
+
72
+ if (shouldUpdateConsumers) {
73
+ this.context.updateObservers();
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Function to update the active theme classname to update the theme tokens
79
+ * as CSS variables on the web component.
80
+ */
81
+ private updateActiveThemeClass() {
82
+ // remove all existing theme classes from the classList:
83
+ this.classList.remove(...this.themes.split(' '));
84
+ // add current theme class to classList:
85
+ this.classList.add(this.theme);
86
+ }
87
+
88
+ public static override styles = styles;
89
+ }
90
+
91
+ export default ThemeProvider;
@@ -0,0 +1,32 @@
1
+ /* eslint-disable implicit-arrow-linebreak */
2
+ import utils from '../../utils/tag-name';
3
+ import type { Theme } from './themeprovider.types';
4
+ import themeProviderUtils from './themeprovider.utils';
5
+
6
+ const TAG_NAME = utils.constructTagName('themeprovider');
7
+
8
+ // Some themes are disabled until tokens are available for those themes
9
+ const THEME_NAMES = {
10
+ // DARK_BRONZE: 'darkBronze' as const,
11
+ // DARK_INDIGO: 'darkIndigo' as const,
12
+ // DARK_JADE: 'darkJade' as const,
13
+ // DARK_LAVENDER: 'darkLavender' as const,
14
+ // DARK_ROSE: 'darkRose' as const,
15
+ DARK_WEBEX: 'darkWebex' as const,
16
+ // LIGHT_BRONZE: 'lightBronze' as const,
17
+ // LIGHT_INDIGO: 'lightIndigo' as const,
18
+ // LIGHT_JADE: 'lightJade' as const,
19
+ // LIGHT_LAVENDER: 'lightLavender' as const,
20
+ // LIGHT_ROSE: 'lightRose' as const,
21
+ LIGHT_WEBEX: 'lightWebex' as const,
22
+ };
23
+
24
+ const THEMES = Object.values(THEME_NAMES).map(
25
+ (themeName) => themeProviderUtils.getFullQualifiedTheme(themeName),
26
+ );
27
+
28
+ const DEFAULTS = {
29
+ THEME: themeProviderUtils.getFullQualifiedTheme(THEME_NAMES.DARK_WEBEX) as Theme<typeof THEME_NAMES.DARK_WEBEX>,
30
+ };
31
+
32
+ export { DEFAULTS, THEME_NAMES, THEMES, TAG_NAME };
@@ -0,0 +1,18 @@
1
+ import { createContext } from '@lit-labs/context';
2
+
3
+ import { TAG_NAME } from './themeprovider.constants';
4
+ import type { Theme } from './themeprovider.types';
5
+
6
+ class ThemeProviderContext {
7
+ public theme?: Theme;
8
+
9
+ // create typed lit context as part of the ThemeProviderContext
10
+ public static context = createContext<ThemeProviderContext>(TAG_NAME);
11
+
12
+ // constructor to allow setting the defaultTheme
13
+ constructor(defaultTheme?: Theme) {
14
+ this.theme = defaultTheme;
15
+ }
16
+ }
17
+
18
+ export default ThemeProviderContext;
@@ -0,0 +1,89 @@
1
+ import { expect } from '@playwright/test';
2
+ import { ComponentsPage, test } from '../../../config/playwright/setup';
3
+ import steps from '../../../config/playwright/setup/steps/accessibility';
4
+ import { THEME_NAMES } from './themeprovider.constants';
5
+ import type { ThemeName } from './themeprovider.types';
6
+ import utils from './themeprovider.utils';
7
+
8
+ test.beforeEach(async ({ componentsPage, theme }) => {
9
+ const themeClass = utils.getFullQualifiedTheme(theme);
10
+ await componentsPage.mount({
11
+ html: `
12
+ <mdc-themeprovider class="themeWrapper" id="local" theme="${themeClass}">
13
+ <p>Current theme: ${themeClass}</p>
14
+ <div>
15
+ <div class="colorBox" style="background: var(--mds-color-theme-text-accent-normal);"></div>
16
+ <div class="colorBox" style="background: var(--mds-color-theme-text-warning-normal);"></div>
17
+ <div class="colorBox" style="background: var(--mds-color-theme-background-alert-success-normal);"></div>
18
+ </div>
19
+ </mdc-themeprovider>
20
+ `,
21
+ });
22
+ });
23
+
24
+ const testToRun = async (componentsPage: ComponentsPage, theme: ThemeName) => {
25
+ const themeprovider = componentsPage.page.locator('mdc-themeprovider#local');
26
+
27
+ // get fully qualified theme
28
+ const themeClass = utils.getFullQualifiedTheme(theme);
29
+ const oppositeThemeClass = themeClass.includes('darkWebex')
30
+ ? utils.getFullQualifiedTheme('lightWebex')
31
+ : utils.getFullQualifiedTheme('darkWebex');
32
+
33
+ // initial check for the themeprovider be visible on the screen:
34
+ await themeprovider.waitFor();
35
+
36
+ /**
37
+ * ACCESSIBILITY
38
+ */
39
+ await test.step('accessibility', async () => {
40
+ await steps.automaticA11yCheckStep(componentsPage);
41
+ });
42
+
43
+ /**
44
+ * VISUAL REGRESSION
45
+ */
46
+ // TODO: fix visual regression test on CI
47
+ // await test.step('visual-regression', async () => {
48
+ // await test.step('matches screenshot of element', async () => {
49
+ // await componentsPage.visualRegression.takeScreenshot(`mdc-themeprovider-${theme}`, {
50
+ // element: themeprovider,
51
+ // });
52
+ // });
53
+ // });
54
+
55
+ /**
56
+ * ATTRIBUTES
57
+ */
58
+ await test.step('attributes', async () => {
59
+ await test.step('attribute theme should be present on component by default', async () => {
60
+ expect(await themeprovider.getAttribute('theme')).toBe(themeClass);
61
+ });
62
+
63
+ await test.step('corresponding theme class should be present on component by default', async () => {
64
+ expect(await themeprovider.getAttribute('class')).toContain(themeClass);
65
+ expect(await themeprovider.getAttribute('class')).not.toContain(oppositeThemeClass);
66
+ });
67
+ });
68
+ };
69
+
70
+ // test.describe('mdc-themeprovider', () => {
71
+ // test.use({
72
+ // theme: THEME_NAMES.DARK_WEBEX,
73
+ // });
74
+
75
+ // test('dark', async ({ componentsPage, theme }) => {
76
+ // await testToRun(componentsPage, theme);
77
+ // });
78
+ // });
79
+
80
+ test.describe('mdc-themeprovider', () => {
81
+ test.use({
82
+ theme: THEME_NAMES.LIGHT_WEBEX,
83
+ });
84
+
85
+ // TODO: fix e2e test
86
+ test.fixme('light', async ({ componentsPage, theme }) => {
87
+ await testToRun(componentsPage, theme);
88
+ });
89
+ });
@@ -0,0 +1,22 @@
1
+ .themeWrapper {
2
+ display: flex;
3
+ flex-direction: column;
4
+ width: calc(30%);
5
+ background: var(--mds-color-theme-background-solid-primary-normal);
6
+ padding: 10px;
7
+ border: 1px solid var(--mds-color-theme-text-primary-normal)
8
+ }
9
+
10
+ .themeWrapper+.themeWrapper {
11
+ margin-top: 10px;
12
+ }
13
+
14
+ .colorBox {
15
+ width: 100%;
16
+ height: 10px;
17
+ border-radius: 5px;
18
+ }
19
+
20
+ .colorBox+.colorBox {
21
+ margin-top: 10px;
22
+ }