@govtechsg/sgds-web-component 0.0.7 → 0.0.10

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 (263) hide show
  1. package/.github/workflows/publish-latest.yml +22 -0
  2. package/.github/workflows/publish-pr.yml +28 -0
  3. package/.husky/commit-msg +4 -0
  4. package/.husky/prepare-commit-msg +8 -0
  5. package/.storybook/main.js +16 -0
  6. package/.storybook/preview-head.html +11 -0
  7. package/.storybook/preview.js +9 -0
  8. package/.vscode/settings.json +7 -0
  9. package/CONTRIBUTING.md +56 -0
  10. package/LICENSE +20 -0
  11. package/amplify.yml +22 -0
  12. package/commitlint.config.js +1 -0
  13. package/coverage/lcov-report/base.css +224 -0
  14. package/coverage/lcov-report/block-navigation.js +87 -0
  15. package/coverage/lcov-report/button-element.scss.html +112 -0
  16. package/coverage/lcov-report/button-element.ts.html +145 -0
  17. package/coverage/lcov-report/favicon.png +0 -0
  18. package/coverage/lcov-report/index.html +116 -0
  19. package/coverage/lcov-report/prettify.css +1 -0
  20. package/coverage/lcov-report/prettify.js +2 -0
  21. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  22. package/coverage/lcov-report/sorter.js +196 -0
  23. package/coverage/lcov.info +32 -0
  24. package/index.html +430 -0
  25. package/{Button → lib/Button}/index.d.ts +0 -0
  26. package/{Button → lib/Button}/index.js +304 -39
  27. package/lib/Button/index.js.map +1 -0
  28. package/{Button → lib/Button}/package.json +0 -0
  29. package/lib/Button/sgds-button.d.ts +48 -0
  30. package/lib/Card/index.d.ts +1 -0
  31. package/lib/Card/index.js +6150 -0
  32. package/lib/Card/index.js.map +1 -0
  33. package/lib/Card/package.json +7 -0
  34. package/lib/Card/sgds-action-card.d.ts +20 -0
  35. package/lib/Checkbox/index.d.ts +1 -0
  36. package/lib/Checkbox/index.js +6366 -0
  37. package/lib/Checkbox/index.js.map +1 -0
  38. package/lib/Checkbox/package.json +7 -0
  39. package/lib/Checkbox/sgds-checkbox.d.ts +36 -0
  40. package/lib/Dropdown/index.d.ts +3 -0
  41. package/{Mainnav → lib/Dropdown}/index.js +2786 -9258
  42. package/lib/Dropdown/index.js.map +1 -0
  43. package/lib/Dropdown/package.json +7 -0
  44. package/lib/Dropdown/sgds-dropdown-item.d.ts +7 -0
  45. package/lib/Dropdown/sgds-dropdown.d.ts +7 -0
  46. package/{Footer → lib/Footer}/index.d.ts +0 -0
  47. package/{Footer → lib/Footer}/index.js +111 -95
  48. package/lib/Footer/index.js.map +1 -0
  49. package/{Footer → lib/Footer}/package.json +0 -0
  50. package/{Footer → lib/Footer}/sgds-footer.d.ts +2 -2
  51. package/lib/Input/index.d.ts +1 -0
  52. package/lib/Input/index.js +6656 -0
  53. package/lib/Input/index.js.map +1 -0
  54. package/lib/Input/package.json +7 -0
  55. package/lib/Input/sgds-input.d.ts +42 -0
  56. package/{Mainnav → lib/Mainnav}/index.d.ts +1 -0
  57. package/{index.js → lib/Mainnav/index.js} +3876 -23415
  58. package/lib/Mainnav/index.js.map +1 -0
  59. package/{Mainnav → lib/Mainnav}/package.json +0 -0
  60. package/lib/Mainnav/sgds-mainnav-dropdown.d.ts +5 -0
  61. package/lib/Mainnav/sgds-mainnav-item.d.ts +4 -0
  62. package/{Mainnav → lib/Mainnav}/sgds-mainnav.d.ts +3 -2
  63. package/{Masthead → lib/Masthead}/index.d.ts +0 -0
  64. package/{Masthead → lib/Masthead}/index.js +140 -114
  65. package/lib/Masthead/index.js.map +1 -0
  66. package/{Masthead → lib/Masthead}/package.json +0 -0
  67. package/{Masthead → lib/Masthead}/sgds-masthead.d.ts +1 -1
  68. package/lib/Modal/index.d.ts +1 -0
  69. package/lib/Modal/index.js +6432 -0
  70. package/lib/Modal/index.js.map +1 -0
  71. package/lib/Modal/package.json +7 -0
  72. package/lib/Modal/sgds-modal.d.ts +28 -0
  73. package/lib/QuantityToggle/index.d.ts +1 -0
  74. package/lib/QuantityToggle/index.js +7049 -0
  75. package/lib/QuantityToggle/index.js.map +1 -0
  76. package/lib/QuantityToggle/package.json +7 -0
  77. package/lib/QuantityToggle/sgds-quantitytoggle.d.ts +30 -0
  78. package/lib/Radio/index.d.ts +2 -0
  79. package/lib/Radio/index.js +12607 -0
  80. package/lib/Radio/index.js.map +1 -0
  81. package/lib/Radio/package.json +7 -0
  82. package/lib/Radio/sgds-radio.d.ts +31 -0
  83. package/lib/Radio/sgds-radiogroup.d.ts +41 -0
  84. package/{Sidenav → lib/Sidenav}/index.d.ts +0 -0
  85. package/{Sidenav → lib/Sidenav}/index.js +2266 -2171
  86. package/lib/Sidenav/index.js.map +1 -0
  87. package/{Sidenav → lib/Sidenav}/package.json +0 -0
  88. package/{Sidenav → lib/Sidenav}/sgds-sidenav-item.d.ts +2 -1
  89. package/lib/Sidenav/sgds-sidenav-link.d.ts +4 -0
  90. package/{Sidenav → lib/Sidenav}/sgds-sidenav.d.ts +1 -1
  91. package/lib/Tab/index.d.ts +3 -0
  92. package/lib/Tab/index.js +13557 -0
  93. package/lib/Tab/index.js.map +1 -0
  94. package/lib/Tab/package.json +7 -0
  95. package/lib/Tab/sgds-tab.d.ts +26 -0
  96. package/lib/Tab/sgds-tabgroup.d.ts +47 -0
  97. package/lib/Tab/sgds-tabpanel.d.ts +25 -0
  98. package/lib/Textarea/index.d.ts +1 -0
  99. package/lib/Textarea/index.js +6696 -0
  100. package/lib/Textarea/index.js.map +1 -0
  101. package/lib/Textarea/package.json +7 -0
  102. package/lib/Textarea/sgds-textarea.d.ts +53 -0
  103. package/lib/index.d.ts +16 -0
  104. package/lib/index.js +134580 -0
  105. package/lib/index.js.map +1 -0
  106. package/lib/umd/index.js +134587 -0
  107. package/lib/umd/index.js.map +1 -0
  108. package/lib/utils/animate.d.ts +10 -0
  109. package/lib/utils/animation-registry.d.ts +18 -0
  110. package/{utils → lib/utils}/breakpoints.d.ts +0 -0
  111. package/lib/utils/card-element.d.ts +11 -0
  112. package/lib/utils/defaultvalue.d.ts +2 -0
  113. package/lib/utils/dropdown-element.d.ts +37 -0
  114. package/lib/utils/event.d.ts +2 -0
  115. package/lib/utils/form.d.ts +38 -0
  116. package/{utils → lib/utils}/generateId.d.ts +0 -0
  117. package/lib/utils/link-element.d.ts +7 -0
  118. package/lib/utils/mergeDeep.d.ts +2 -0
  119. package/lib/utils/modal.d.ts +12 -0
  120. package/lib/utils/object.d.ts +2 -0
  121. package/lib/utils/offset.d.ts +4 -0
  122. package/lib/utils/scroll.d.ts +13 -0
  123. package/{utils → lib/utils}/sgds-element.d.ts +0 -0
  124. package/lib/utils/slot.d.ts +22 -0
  125. package/lib/utils/tabbable.d.ts +8 -0
  126. package/lib/utils/watch.d.ts +14 -0
  127. package/mocks/dropdown.d.ts +4 -0
  128. package/mocks/dropdown.ts +27 -0
  129. package/mocks/link.d.ts +3 -0
  130. package/mocks/link.ts +6 -0
  131. package/package.json +65 -10
  132. package/rollup.config.js +73 -0
  133. package/rollup.test.config.js +42 -0
  134. package/scripts/buildUtils.js +30 -0
  135. package/scripts/frankBuild.js +49 -0
  136. package/src/Button/index.ts +1 -0
  137. package/src/Button/sgds-button.scss +28 -0
  138. package/src/Button/sgds-button.ts +153 -0
  139. package/src/Card/index.ts +1 -0
  140. package/src/Card/sgds-action-card.scss +27 -0
  141. package/src/Card/sgds-action-card.ts +115 -0
  142. package/src/Checkbox/index.ts +1 -0
  143. package/src/Checkbox/sgds-checkbox.scss +4 -0
  144. package/src/Checkbox/sgds-checkbox.ts +149 -0
  145. package/src/Dropdown/index.ts +3 -0
  146. package/src/Dropdown/sgds-dropdown-item.ts +39 -0
  147. package/src/Dropdown/sgds-dropdown.scss +5 -0
  148. package/src/Dropdown/sgds-dropdown.ts +54 -0
  149. package/src/Footer/index.ts +3 -0
  150. package/src/Footer/sgds-footer.scss +5 -0
  151. package/src/Footer/sgds-footer.ts +121 -0
  152. package/src/Input/index.ts +1 -0
  153. package/src/Input/sgds-input.scss +20 -0
  154. package/src/Input/sgds-input.ts +178 -0
  155. package/src/Mainnav/index.ts +4 -0
  156. package/src/Mainnav/sgds-mainnav-dropdown.scss +13 -0
  157. package/src/Mainnav/sgds-mainnav-dropdown.ts +45 -0
  158. package/src/Mainnav/sgds-mainnav-item.scss +24 -0
  159. package/src/Mainnav/sgds-mainnav-item.ts +8 -0
  160. package/src/Mainnav/sgds-mainnav.scss +39 -0
  161. package/src/Mainnav/sgds-mainnav.ts +183 -0
  162. package/src/Masthead/index.ts +1 -0
  163. package/src/Masthead/sgds-masthead.scss +217 -0
  164. package/src/Masthead/sgds-masthead.ts +189 -0
  165. package/src/Modal/index.ts +1 -0
  166. package/src/Modal/sgds-modal.scss +128 -0
  167. package/src/Modal/sgds-modal.ts +309 -0
  168. package/src/QuantityToggle/index.ts +1 -0
  169. package/src/QuantityToggle/sgds-quantitytoggle.scss +10 -0
  170. package/src/QuantityToggle/sgds-quantitytoggle.ts +130 -0
  171. package/src/Radio/index.ts +2 -0
  172. package/src/Radio/sgds-radio.scss +5 -0
  173. package/src/Radio/sgds-radio.ts +120 -0
  174. package/src/Radio/sgds-radiogroup.scss +22 -0
  175. package/src/Radio/sgds-radiogroup.ts +221 -0
  176. package/src/Sidenav/index.ts +4 -0
  177. package/src/Sidenav/sgds-sidenav-item.scss +73 -0
  178. package/src/Sidenav/sgds-sidenav-item.ts +145 -0
  179. package/src/Sidenav/sgds-sidenav-link.scss +25 -0
  180. package/src/Sidenav/sgds-sidenav-link.ts +8 -0
  181. package/src/Sidenav/sgds-sidenav.scss +6 -0
  182. package/src/Sidenav/sgds-sidenav.ts +33 -0
  183. package/src/Tab/index.ts +3 -0
  184. package/src/Tab/sgds-tab.scss +84 -0
  185. package/src/Tab/sgds-tab.ts +87 -0
  186. package/src/Tab/sgds-tabgroup.scss +198 -0
  187. package/src/Tab/sgds-tabgroup.ts +295 -0
  188. package/src/Tab/sgds-tabpanel.scss +12 -0
  189. package/src/Tab/sgds-tabpanel.ts +55 -0
  190. package/src/Textarea/index.ts +1 -0
  191. package/src/Textarea/sgds-textarea.scss +23 -0
  192. package/src/Textarea/sgds-textarea.ts +201 -0
  193. package/src/index.ts +16 -0
  194. package/src/utils/animate.ts +69 -0
  195. package/src/utils/animation-registry.ts +71 -0
  196. package/src/utils/base.scss +14 -0
  197. package/src/utils/breakpoints.ts +5 -0
  198. package/src/utils/card-element.ts +42 -0
  199. package/src/utils/components.style.scss +531 -0
  200. package/src/utils/defaultvalue.ts +51 -0
  201. package/src/utils/dropdown-element.ts +244 -0
  202. package/src/utils/event.ts +13 -0
  203. package/src/utils/form.ts +183 -0
  204. package/src/utils/generateId.ts +4 -0
  205. package/src/utils/link-element.ts +34 -0
  206. package/src/utils/mergeDeep.ts +22 -0
  207. package/src/utils/modal.ts +64 -0
  208. package/src/utils/object.ts +2 -0
  209. package/src/utils/offset.ts +6 -0
  210. package/src/utils/scroll.ts +57 -0
  211. package/src/utils/sgds-element.ts +18 -0
  212. package/src/utils/slot.ts +102 -0
  213. package/src/utils/tabbable.ts +81 -0
  214. package/src/utils/watch.ts +62 -0
  215. package/stories/ActionCard.stories.mdx +199 -0
  216. package/stories/Button.stories.mdx +194 -0
  217. package/stories/Checkbox.stories.mdx +196 -0
  218. package/stories/Dropdown.stories.mdx +152 -0
  219. package/stories/Footer.stories.mdx +261 -0
  220. package/stories/Input.stories.mdx +236 -0
  221. package/stories/MainNav.stories.mdx +169 -0
  222. package/stories/Masthead.stories.mdx +112 -0
  223. package/stories/Modal.stories.mdx +103 -0
  224. package/stories/QuantityToggle.stories.mdx +97 -0
  225. package/stories/Radio.stories.mdx +262 -0
  226. package/stories/Sample.stories.js +29 -0
  227. package/stories/Sample.stories.mdx +33 -0
  228. package/stories/SideNav.stories.mdx +245 -0
  229. package/stories/common.js +185 -0
  230. package/stories/textarea.stories.mdx +253 -0
  231. package/test/button.element.test.ts +185 -0
  232. package/test/checkbox.test.ts +240 -0
  233. package/test/dropdown.test.ts +637 -0
  234. package/test/footer.test.ts +181 -0
  235. package/test/generateId.test.ts +18 -0
  236. package/test/input.element.test.ts +316 -0
  237. package/test/link-element.test.ts +38 -0
  238. package/test/mainnav.test.ts +313 -0
  239. package/test/masthead.test.ts +116 -0
  240. package/test/modal.test.ts +149 -0
  241. package/test/quantitytoggle.test.ts +76 -0
  242. package/test/radio.test.ts +310 -0
  243. package/test/selectable-card.test.ts +159 -0
  244. package/test/sidenav.test.ts +390 -0
  245. package/test/tab.test.ts +76 -0
  246. package/test/textarea.test.ts +126 -0
  247. package/tsconfig.json +26 -0
  248. package/tsconfig.test.json +24 -0
  249. package/typings/scss.d.ts +5 -0
  250. package/web-dev-server.config.mjs +7 -0
  251. package/web-test-runner.config.mjs +47 -0
  252. package/Button/index.js.map +0 -1
  253. package/Button/sgds-button.d.ts +0 -23
  254. package/Footer/index.js.map +0 -1
  255. package/Mainnav/index.js.map +0 -1
  256. package/Mainnav/sgds-mainnav-item.d.ts +0 -7
  257. package/Masthead/index.js.map +0 -1
  258. package/Sidenav/index.js.map +0 -1
  259. package/Sidenav/sgds-sidenav-link.d.ts +0 -7
  260. package/index.d.ts +0 -5
  261. package/index.js.map +0 -1
  262. package/umd/index.js +0 -52092
  263. package/umd/index.js.map +0 -1
@@ -0,0 +1,102 @@
1
+ import type { ReactiveController, ReactiveControllerHost } from 'lit';
2
+
3
+ export class HasSlotController implements ReactiveController {
4
+ host: ReactiveControllerHost & Element;
5
+ slotNames: string[] = [];
6
+
7
+ constructor(host: ReactiveControllerHost & Element, ...slotNames: string[]) {
8
+ (this.host = host).addController(this);
9
+ this.slotNames = slotNames;
10
+ this.handleSlotChange = this.handleSlotChange.bind(this);
11
+ }
12
+
13
+ private hasDefaultSlot() {
14
+ return [...this.host.childNodes].some(node => {
15
+ if (node.nodeType === node.TEXT_NODE && node.textContent!.trim() !== '') {
16
+ return true;
17
+ }
18
+
19
+ if (node.nodeType === node.ELEMENT_NODE) {
20
+ const el = node as HTMLElement;
21
+ const tagName = el.tagName.toLowerCase();
22
+
23
+ // Ignore visually hidden elements since they aren't rendered
24
+ if (tagName === 'sl-visually-hidden') {
25
+ return false;
26
+ }
27
+
28
+ // If it doesn't have a slot attribute, it's part of the default slot
29
+ if (!el.hasAttribute('slot')) {
30
+ return true;
31
+ }
32
+ }
33
+
34
+ return false;
35
+ });
36
+ }
37
+
38
+ private hasNamedSlot(name: string) {
39
+ return this.host.querySelector(`:scope > [slot="${name}"]`) !== null;
40
+ }
41
+
42
+ test(slotName: string) {
43
+ return slotName === '[default]' ? this.hasDefaultSlot() : this.hasNamedSlot(slotName);
44
+ }
45
+
46
+ hostConnected() {
47
+ this.host.shadowRoot!.addEventListener('slotchange', this.handleSlotChange);
48
+ }
49
+
50
+ hostDisconnected() {
51
+ this.host.shadowRoot!.removeEventListener('slotchange', this.handleSlotChange);
52
+ }
53
+
54
+ handleSlotChange(event: Event) {
55
+ const slot = event.target as HTMLSlotElement;
56
+
57
+ if ((this.slotNames.includes('[default]') && !slot.name) || (slot.name && this.slotNames.includes(slot.name))) {
58
+ this.host.requestUpdate();
59
+ }
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Given a slot, this function iterates over all of its assigned element and text nodes and returns the concatenated
65
+ * HTML as a string. This is useful because we can't use slot.innerHTML as an alternative.
66
+ */
67
+ export function getInnerHTML(slot: HTMLSlotElement): string {
68
+ const nodes = slot.assignedNodes({ flatten: true });
69
+ let html = '';
70
+
71
+ [...nodes].forEach(node => {
72
+ if (node.nodeType === Node.ELEMENT_NODE) {
73
+ html += (node as HTMLElement).outerHTML;
74
+ }
75
+
76
+ if (node.nodeType === Node.TEXT_NODE) {
77
+ html += node.textContent;
78
+ }
79
+ });
80
+
81
+ return html;
82
+ }
83
+
84
+ /**
85
+ * Given a slot, this function iterates over all of its assigned text nodes and returns the concatenated text as a
86
+ * string. This is useful because we can't use slot.textContent as an alternative.
87
+ */
88
+ export function getTextContent(slot: HTMLSlotElement | undefined | null): string {
89
+ if (!slot) {
90
+ return '';
91
+ }
92
+ const nodes = slot.assignedNodes({ flatten: true });
93
+ let text = '';
94
+
95
+ [...nodes].forEach(node => {
96
+ if (node.nodeType === Node.TEXT_NODE) {
97
+ text += node.textContent;
98
+ }
99
+ });
100
+
101
+ return text;
102
+ }
@@ -0,0 +1,81 @@
1
+ /** Determines if the specified element is tabbable using heuristics inspired by https://github.com/focus-trap/tabbable */
2
+ function isTabbable(el: HTMLElement) {
3
+ const tag = el.tagName.toLowerCase();
4
+
5
+ // Elements with a -1 tab index are not tabbable
6
+ if (el.getAttribute('tabindex') === '-1') {
7
+ return false;
8
+ }
9
+
10
+ // Elements with a disabled attribute are not tabbable
11
+ if (el.hasAttribute('disabled')) {
12
+ return false;
13
+ }
14
+
15
+ // Elements with aria-disabled are not tabbable
16
+ if (el.hasAttribute('aria-disabled') && el.getAttribute('aria-disabled') !== 'false') {
17
+ return false;
18
+ }
19
+
20
+ // Radios without a checked attribute are not tabbable
21
+ if (tag === 'input' && el.getAttribute('type') === 'radio' && !el.hasAttribute('checked')) {
22
+ return false;
23
+ }
24
+
25
+ // Elements that are hidden have no offsetParent and are not tabbable
26
+ if (el.offsetParent === null) {
27
+ return false;
28
+ }
29
+
30
+ // Elements without visibility are not tabbable
31
+ if (window.getComputedStyle(el).visibility === 'hidden') {
32
+ return false;
33
+ }
34
+
35
+ // Audio and video elements with the controls attribute are tabbable
36
+ if ((tag === 'audio' || tag === 'video') && el.hasAttribute('controls')) {
37
+ return true;
38
+ }
39
+
40
+ // Elements with a tabindex other than -1 are tabbable
41
+ if (el.hasAttribute('tabindex')) {
42
+ return true;
43
+ }
44
+
45
+ // Elements with a contenteditable attribute are tabbable
46
+ if (el.hasAttribute('contenteditable') && el.getAttribute('contenteditable') !== 'false') {
47
+ return true;
48
+ }
49
+
50
+ // At this point, the following elements are considered tabbable
51
+ return ['button', 'input', 'select', 'textarea', 'a', 'audio', 'video', 'summary'].includes(tag);
52
+ }
53
+
54
+ /**
55
+ * Returns the first and last bounding elements that are tabbable. This is more performant than checking every single
56
+ * element because it short-circuits after finding the first and last ones.
57
+ */
58
+ export function getTabbableBoundary(root: HTMLElement | ShadowRoot) {
59
+ const allElements: HTMLElement[] = [];
60
+
61
+ function walk(el: HTMLElement | ShadowRoot) {
62
+ if (el instanceof HTMLElement) {
63
+ allElements.push(el);
64
+
65
+ if (el.shadowRoot !== null && el.shadowRoot.mode === 'open') {
66
+ walk(el.shadowRoot);
67
+ }
68
+ }
69
+
70
+ [...el.children].forEach((e: HTMLElement) => walk(e));
71
+ }
72
+
73
+ // Collect all elements including the root
74
+ walk(root);
75
+
76
+ // Find the first and last tabbable elements
77
+ const start = allElements.find(el => isTabbable(el)) ?? null;
78
+ const end = allElements.reverse().find(el => isTabbable(el)) ?? null;
79
+
80
+ return { start, end };
81
+ }
@@ -0,0 +1,62 @@
1
+ // @watch decorator
2
+ //
3
+ // Runs when an observed property changes, e.g. @property or @state, but before the component updates.
4
+ //
5
+ // To wait for an update to complete after a change occurs, use `await this.updateComplete` in the handler. To start
6
+ // watching after the initial update/render, use `{ waitUntilFirstUpdate: true }` or `this.hasUpdated` in the handler.
7
+ //
8
+ // Usage:
9
+ //
10
+ // @watch('propName')
11
+ // handlePropChange(oldValue, newValue) {
12
+ // ...
13
+ // }
14
+
15
+ import type { LitElement } from "lit";
16
+
17
+ type UpdateHandler = (prev?: unknown, next?: unknown) => void;
18
+
19
+ type NonUndefined<A> = A extends undefined ? never : A;
20
+
21
+ type UpdateHandlerFunctionKeys<T extends object> = {
22
+ [K in keyof T]-?: NonUndefined<T[K]> extends UpdateHandler ? K : never;
23
+ }[keyof T];
24
+
25
+ interface WatchOptions {
26
+ /**
27
+ * If true, will only start watching after the initial update/render
28
+ */
29
+ waitUntilFirstUpdate?: boolean;
30
+ }
31
+
32
+ export function watch(propName: string, options?: WatchOptions) {
33
+ const resolvedOptions: Required<WatchOptions> = {
34
+ waitUntilFirstUpdate: false,
35
+ ...options,
36
+ };
37
+ return <ElemClass extends LitElement>(
38
+ proto: ElemClass,
39
+ decoratedFnName: UpdateHandlerFunctionKeys<ElemClass>
40
+ ): void => {
41
+ // @ts-expect-error -- update is a protected property
42
+ const { update } = proto;
43
+ if (propName in proto) {
44
+ const propNameKey = propName as keyof ElemClass;
45
+ // @ts-expect-error -- update is a protected property
46
+ proto.update = function (this: ElemClass, changedProps: Map<keyof ElemClass, ElemClass[keyof ElemClass]>) {
47
+ if (changedProps.has(propNameKey)) {
48
+ const oldValue = changedProps.get(propNameKey);
49
+ const newValue = this[propNameKey];
50
+
51
+ if (oldValue !== newValue) {
52
+ if (!resolvedOptions.waitUntilFirstUpdate || this.hasUpdated) {
53
+ (this[decoratedFnName] as unknown as UpdateHandler)(oldValue, newValue);
54
+ }
55
+ }
56
+ }
57
+
58
+ update.call(this, changedProps);
59
+ };
60
+ }
61
+ };
62
+ }
@@ -0,0 +1,199 @@
1
+ import { Canvas, Meta, Story, ArgsTable } from "@storybook/addon-docs";
2
+ import { html } from "lit-html";
3
+ import "../lib/Card";
4
+
5
+ <Meta
6
+ title='Components/Card/Action Card'
7
+ argTypes={{
8
+ bgColor: {
9
+ control: "select",
10
+ options: [
11
+ "primary",
12
+ "secondary",
13
+ "success",
14
+ "danger",
15
+ "warning",
16
+ "info",
17
+ "dark",
18
+ "light",
19
+ ],
20
+ defaultValue: "",
21
+ table: { category: "props" },
22
+ description: "Toggles the background color of the card",
23
+ },
24
+ textColor: {
25
+ control: "select",
26
+ options: [
27
+ "primary",
28
+ "secondary",
29
+ "success",
30
+ "danger",
31
+ "warning",
32
+ "info",
33
+ "dark",
34
+ "light",
35
+ "white",
36
+ "muted",
37
+ ],
38
+ defaultValue: "",
39
+ table: { category: "props" },
40
+ description: "Toggles the text color of the card",
41
+ },
42
+ borderColor: {
43
+ control: "select",
44
+ options: [
45
+ "primary",
46
+ "secondary",
47
+ "success",
48
+ "danger",
49
+ "warning",
50
+ "info",
51
+ "dark",
52
+ "light",
53
+ ],
54
+ defaultValue: "",
55
+ table: { category: "props" },
56
+ description: "Toggles the border color of the card",
57
+ },
58
+ active: {
59
+ control: "boolean",
60
+ description:
61
+ "By default, with variant card-action, set the active stylings when input is checked",
62
+ table: {
63
+ category: "props",
64
+ defaultValue: { summary: false },
65
+ type: {
66
+ summary: "boolean",
67
+ },
68
+ },
69
+ },
70
+ disabled: {
71
+ control: "boolean",
72
+ defaultValue: false,
73
+ table: { category: "props" },
74
+ description: "The disabled state of the card input",
75
+ },
76
+ iconName: {
77
+ control: "text",
78
+ defaultValue: "box",
79
+ table: {
80
+ category: "props",
81
+ type: { summary: "string" },
82
+ },
83
+ description:
84
+ "Optional. The name of the [bootstrap's icon](https://icons.getbootstrap.com/)",
85
+ },
86
+ cardSubtitleSlot: {
87
+ name: "card-subtitle",
88
+ control: "text",
89
+ defaultValue: "Laptop",
90
+ description: "Insert text here for card-subtitle slot",
91
+ table: {
92
+ category: "slots",
93
+ type: {
94
+ summary: "string",
95
+ },
96
+ },
97
+ },
98
+ cardTitleSlot: {
99
+ name: "card-title",
100
+ control: "text",
101
+ defaultValue: "Apple",
102
+ description: "Insert text here for card-title slot",
103
+ table: {
104
+ category: "slots",
105
+ type: {
106
+ summary: "string",
107
+ },
108
+ },
109
+ },
110
+ cardTextSlot: {
111
+ name: "card-text",
112
+ control: "text",
113
+ defaultValue: "Macbook Pro M1",
114
+ description: "Insert text here for card-text slot",
115
+ table: {
116
+ category: "slots",
117
+ type: {
118
+ summary: "string",
119
+ },
120
+ },
121
+ },
122
+ type: {
123
+ control: "select",
124
+ options: ["checkbox", "radio"],
125
+ defaultValue: "checkbox",
126
+ description:
127
+ "For variant card-action, change the type to radio or checkbox by default",
128
+ table: {
129
+ category: "props",
130
+ type: {
131
+ summary: "string",
132
+ },
133
+ },
134
+ },
135
+ }}
136
+ />
137
+
138
+ export const Template = (args) =>
139
+ html`
140
+ <sgds-action-card
141
+ .active=${args.active}
142
+ .bgColor=${args.bgColor}
143
+ .borderColor=${args.borderColor}
144
+ .textColor=${args.textColor}
145
+ .disabled=${args.disabled}
146
+ .iconName=${args.iconName}
147
+ .type=${args.type}
148
+ >
149
+ <span slot="card-subtitle">${args.cardSubtitleSlot}</span>
150
+ <span slot="card-title">${args.cardTitleSlot}</span>
151
+ <span slot="card-text">${args.cardTextSlot}</span>
152
+ </sgds-action-card>
153
+ `;
154
+ export const TemplateType = (args) =>
155
+ html`
156
+
157
+ <div class="col">
158
+ <sgds-action-card
159
+ type="radio"
160
+ >
161
+ <span slot="card-subtitle">${args.cardSubtitleSlot}</span>
162
+ <span slot="card-title">${args.cardTitleSlot}</span>
163
+ <span slot="card-text">${args.cardTextSlot}</span>
164
+ </sgds-action-card>
165
+ <div>
166
+ <div class="col">
167
+ <sgds-action-card
168
+ type="checkbox"
169
+ >
170
+ <span slot="card-subtitle">${args.cardSubtitleSlot}</span>
171
+ <span slot="card-title">${args.cardTitleSlot}</span>
172
+ <span slot="card-text">${args.cardTextSlot}</span>
173
+ </sgds-action-card>
174
+ <div>
175
+ `;
176
+
177
+ # SgdsActionCard
178
+
179
+ Action Cards are Cards that are able to perform actions like selection or quantity toggling. Usually with built-in input elements.
180
+
181
+ <Canvas>
182
+ <Story name='Basic'>{Template.bind({})}</Story>
183
+ </Canvas>
184
+
185
+ ## API
186
+
187
+ ```jsx
188
+ import { SgdsActionCard } from "@govtechsg/sgds-web-component";
189
+ ```
190
+
191
+ <ArgsTable story='Basic' />
192
+
193
+ ## Type
194
+
195
+ Use `type` prop : `radio` / `checkbox` to change the selectable card input type
196
+
197
+ <Canvas>
198
+ <Story name='Type'>{TemplateType.bind({})}</Story>
199
+ </Canvas>
@@ -0,0 +1,194 @@
1
+ import { Canvas, Meta, Story, ArgsTable } from "@storybook/addon-docs";
2
+ import { html } from "lit-html";
3
+ import "../lib/Button";
4
+
5
+ <Meta
6
+ title="Components/Button"
7
+ argTypes={{
8
+ buttonClasses: {
9
+ control: "text" ,
10
+ table: { category: "props" },
11
+ description: 'Optional. Can be used to insert any utility classes such as `me-auto`'
12
+ },
13
+ variant: {
14
+ control: 'select',
15
+ options: [
16
+ 'primary',
17
+ 'secondary',
18
+ 'success',
19
+ 'danger',
20
+ 'warning',
21
+ 'info',
22
+ 'dark',
23
+ 'light',
24
+ 'link',
25
+ 'icon',
26
+ 'outline-primary',
27
+ 'outline-secondary',
28
+ 'outline-success',
29
+ 'outline-danger',
30
+ 'outline-warning',
31
+ 'outline-info',
32
+ 'outline-dark',
33
+ 'outline-light',
34
+ ],
35
+ defaultValue: 'primary',
36
+ table: { category: "props" },
37
+ description: "One or more button variant combinations buttons may be one of a variety of visual variants such as: `primary`, `secondary`, `success`, `danger`, `warning`, `info`, `dark`, `light`, `link` as well as `outline` versions (prefixed by `outline-*`)"
38
+ },
39
+ size:{
40
+ control: 'select',
41
+ options:['(default)','sm','lg'],
42
+ description:'Specifies a large or small button',
43
+ table: { category: "props" },
44
+ },
45
+ active:{
46
+ control:'boolean',
47
+ defaultValue: false,
48
+ table: { category: "props" },
49
+ description: 'Manually set the visual state of the button to :active'
50
+ },
51
+ disabled:{
52
+ control: "boolean",
53
+ defaultValue: false,
54
+ table: { category: "props" },
55
+ description:'The disabled state of the button'
56
+ },
57
+ href:{
58
+ control:"text",
59
+ defaultValue: undefined,
60
+ table: { category: "props" },
61
+ description:'When set, the underlying button will be rendered as an `<a>` with this `href` instead of a `<button>`.'
62
+ },
63
+ target:{
64
+ control: 'select',
65
+ options:[
66
+ '_self',
67
+ '_blank',
68
+ '_parent',
69
+ '_top'
70
+ ],
71
+ defaultValue: '_self',
72
+ table: { category: "props" },
73
+ description:'Tells the browser where to open the link. Only used when `href` is set.'
74
+ },
75
+ download:{
76
+ control: 'text',
77
+ table: { category: "props" },
78
+ description: 'Tells the browser to download the linked file as this filename. Only used when `href` is set.'
79
+ },
80
+ form:{
81
+ control: 'text',
82
+ table: {
83
+ category: "props",
84
+ type: { summary: "string" },
85
+ },
86
+ description: 'The "form owner" to associate the button with. If omitted, the closest containing form will be used instead. The value of this attribute must be an id of a form in the same document or shadow root as the button. Read [more](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attr-form)'
87
+ },
88
+ formAction:{
89
+ control: 'text',
90
+ table: {
91
+ category: "props",
92
+ type: { summary: "string" },
93
+ },
94
+ description: "Used to override the form owner's `action` attribute. Read [more](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attr-formaction)"
95
+ },
96
+ formMethod:{
97
+ control: 'select',
98
+ options:['_get','_post'],
99
+ table: {
100
+ category: "props",
101
+ type: { summary: "string" },
102
+ },
103
+ description: "Used to override the form owner's `method` attribute.. Read [more](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attr-formmethod)"
104
+ },
105
+ formNoValidate:{
106
+ control: 'boolean',
107
+ table: {
108
+ category: "props",
109
+ type: { summary: "boolean" },
110
+ },
111
+ description: "Used to override the form owner's `novalidate` attribute. Read [more](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attr-formnovalidate)"
112
+ },
113
+ formTarget:{
114
+ control: 'select',
115
+ options:[
116
+ '_self' , '_blank' , '_parent' , '_top'
117
+ ],
118
+ table: {
119
+ category: "props",
120
+ type: { summary: "string" },
121
+ },
122
+ description: "Used to override the form owner's `target` attribute. Read [more](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attr-formtarget)"
123
+ },
124
+ click:{
125
+ table: {
126
+ category: "Method",
127
+ type: { summary: "Method" },
128
+ },
129
+ description: 'Simulates a click on the button.'
130
+ },
131
+ blur:{
132
+ table: {
133
+ category: "Method",
134
+ type: { summary: "Method" },
135
+ },
136
+ description: 'Removes focus from the button.'
137
+ },
138
+ focus:{
139
+ table: {
140
+ category: "Method",
141
+ type: { summary: "Method" },
142
+ },
143
+ description: 'Sets focus on the button.'
144
+ },
145
+ 'sgds-blur':{
146
+ table: {
147
+ category: "Events",
148
+ type: { summary: "Event" },
149
+ },
150
+ description: 'Removes focus from the button.'
151
+ },
152
+ 'sgds-focus':{
153
+ table: {
154
+ category: "Events",
155
+ type: { summary: "Event" },
156
+ },
157
+ description: 'Sets focus on the button.'
158
+ }
159
+ }}
160
+ />
161
+
162
+ export const Template = (args) =>
163
+ html`
164
+ <sgds-button
165
+ .variant=${args.variant}
166
+ .buttonClasses=${args.buttonClasses}
167
+ .size=${args.size}
168
+ .active=${args.active}
169
+ .disabled=${args.disabled}
170
+ .href=${args.href}
171
+ .target=${args.target}
172
+ .download=${args.download}
173
+ >
174
+ ${args.variant}
175
+ </sgds-button>
176
+ `;
177
+
178
+ # SgdsButton
179
+
180
+ Buttons communicate actions that users can take throughout your portal which can be used in places such as modals, forms and cards
181
+
182
+ <Canvas>
183
+ <Story name="Basic" args={{ variant: "primary" }}>
184
+ {Template.bind({})}
185
+ </Story>
186
+ </Canvas>
187
+
188
+ ## API
189
+
190
+ ```jsx
191
+ import { SgdsButton } from "@govtechsg/sgds-web-component";
192
+ ```
193
+
194
+ <ArgsTable story="Basic" />