@stackoverflow/stacks 1.8.0 → 1.9.0

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 (137) hide show
  1. package/dist/components/activity-indicator/activity-indicator.a11y.test.d.ts +1 -0
  2. package/dist/components/activity-indicator/activity-indicator.visual.test.d.ts +1 -0
  3. package/dist/components/avatar/avatar.a11y.test.d.ts +1 -0
  4. package/dist/components/avatar/avatar.visual.test.d.ts +1 -0
  5. package/dist/{controllers/s-banner.d.ts → components/banner/banner.d.ts} +1 -1
  6. package/dist/components/banner/banner.test.d.ts +1 -0
  7. package/dist/components/banner/banner.visual.test.d.ts +1 -0
  8. package/dist/components/button/button.a11y.test.d.ts +1 -0
  9. package/dist/components/button/button.visual.test.d.ts +1 -0
  10. package/dist/{controllers/s-expandable-control.d.ts → components/expandable/expandable.d.ts} +1 -1
  11. package/dist/components/expandable/expandable.test.d.ts +1 -0
  12. package/dist/{controllers/s-modal.d.ts → components/modal/modal.d.ts} +1 -1
  13. package/dist/{controllers/s-navigation-tablist.d.ts → components/navigation/navigation.d.ts} +1 -1
  14. package/dist/{controllers/s-popover.d.ts → components/popover/popover.d.ts} +1 -1
  15. package/dist/{controllers/s-tooltip.d.ts → components/popover/tooltip.d.ts} +1 -1
  16. package/dist/components/popover/tooltip.test.d.ts +1 -0
  17. package/dist/components/popover/tooltip.visual.test.d.ts +1 -0
  18. package/dist/{controllers/s-table.d.ts → components/table/table.d.ts} +1 -1
  19. package/dist/{controllers/s-toast.d.ts → components/toast/toast.d.ts} +1 -1
  20. package/dist/components/toast/toast.test.d.ts +1 -0
  21. package/dist/components/toast/toast.visual.test.d.ts +1 -0
  22. package/dist/{controllers/s-uploader.d.ts → components/uploader/uploader.d.ts} +1 -1
  23. package/dist/controllers.d.ts +9 -0
  24. package/dist/css/stacks.css +2043 -1989
  25. package/dist/css/stacks.min.css +1 -1
  26. package/dist/index.d.ts +1 -1
  27. package/dist/js/stacks.js +545 -545
  28. package/dist/js/stacks.min.js +1 -1
  29. package/dist/test/test-utils.d.ts +136 -0
  30. package/lib/{css/atomic/borders.less → atomic/border.less} +18 -0
  31. package/lib/{css/base/icons.less → base/icon.less} +0 -9
  32. package/lib/components/activity-indicator/activity-indicator.a11y.test.ts +21 -0
  33. package/lib/components/activity-indicator/activity-indicator.visual.test.ts +23 -0
  34. package/lib/components/avatar/avatar.a11y.test.ts +36 -0
  35. package/lib/components/avatar/avatar.visual.test.ts +54 -0
  36. package/lib/components/banner/banner.less +51 -0
  37. package/lib/{test/s-banner.test.ts → components/banner/banner.test.ts} +7 -3
  38. package/lib/{ts/controllers/s-banner.ts → components/banner/banner.ts} +1 -1
  39. package/lib/components/banner/banner.visual.test.ts +36 -0
  40. package/lib/components/button/button.a11y.test.ts +32 -0
  41. package/lib/{css/components/buttons.less → components/button/button.less} +3 -2
  42. package/lib/components/button/button.visual.test.ts +52 -0
  43. package/lib/{css/components/cards.less → components/card/card.less} +1 -1
  44. package/lib/components/check-control/check-control.less +17 -0
  45. package/lib/components/check-group/check-group.less +19 -0
  46. package/lib/{css/components → components/expandable}/expandable.less +3 -0
  47. package/lib/components/expandable/expandable.test.ts +53 -0
  48. package/lib/{ts/controllers/s-expandable-control.ts → components/expandable/expandable.ts} +1 -1
  49. package/lib/components/input-fill/input-fill.less +35 -0
  50. package/lib/components/input-icon/input-icon.less +45 -0
  51. package/lib/components/input-message/input-message.less +48 -0
  52. package/lib/{css/components/inputs.less → components/input_textarea/input_textarea.less} +0 -131
  53. package/lib/{css/components → components/link}/link.less +2 -2
  54. package/lib/{ts/controllers/s-modal.ts → components/modal/modal.ts} +1 -1
  55. package/lib/{ts/controllers/s-navigation-tablist.ts → components/navigation/navigation.ts} +1 -1
  56. package/lib/{css/components/notices.less → components/notice/notice.less} +0 -89
  57. package/lib/{css/components/popovers.less → components/popover/popover.less} +1 -0
  58. package/lib/{ts/controllers/s-popover.ts → components/popover/popover.ts} +1 -1
  59. package/lib/{test/s-tooltip.test.ts → components/popover/tooltip.test.ts} +6 -2
  60. package/lib/{ts/controllers/s-tooltip.ts → components/popover/tooltip.ts} +2 -2
  61. package/lib/{test/s-tooltip.visual.test.ts → components/popover/tooltip.visual.test.ts} +2 -2
  62. package/lib/{css/components/sidebar-widgets.less → components/sidebar-widget/sidebar-widget.less} +0 -1
  63. package/lib/{css/components → components/table}/table.less +0 -5
  64. package/lib/{ts/controllers/s-table.ts → components/table/table.ts} +1 -1
  65. package/lib/components/table-container/table-container.less +4 -0
  66. package/lib/components/toast/toast.less +35 -0
  67. package/lib/{test/s-toast.test.ts → components/toast/toast.test.ts} +7 -3
  68. package/lib/{ts/controllers/s-toast.ts → components/toast/toast.ts} +1 -1
  69. package/lib/components/toast/toast.visual.test.ts +27 -0
  70. package/lib/{ts/controllers/s-uploader.ts → components/uploader/uploader.ts} +1 -1
  71. package/lib/controllers.ts +33 -0
  72. package/lib/{ts/index.ts → index.ts} +1 -1
  73. package/lib/{css/stacks-dynamic.less → stacks-dynamic.less} +1 -2
  74. package/lib/stacks-static.less +93 -0
  75. package/lib/test/test-utils.ts +444 -0
  76. package/lib/tsconfig.json +1 -1
  77. package/package.json +17 -17
  78. package/dist/controllers/index.d.ts +0 -9
  79. package/lib/css/stacks-static.less +0 -87
  80. package/lib/test/s-avatar.a11y.test.ts +0 -77
  81. package/lib/test/s-banner.visual.test.ts +0 -61
  82. package/lib/test/s-btn.a11y.test.ts +0 -123
  83. package/lib/test/s-btn.visual.test.ts +0 -16
  84. package/lib/test/s-toast.visual.test.ts +0 -48
  85. package/lib/ts/controllers/index.ts +0 -17
  86. /package/lib/{css/atomic/colors.less → atomic/color.less} +0 -0
  87. /package/lib/{css/atomic → atomic}/flex.less +0 -0
  88. /package/lib/{css/atomic → atomic}/gap.less +0 -0
  89. /package/lib/{css/atomic → atomic}/grid.less +0 -0
  90. /package/lib/{css/atomic → atomic}/misc.less +0 -0
  91. /package/lib/{css/atomic → atomic}/spacing.less +0 -0
  92. /package/lib/{css/atomic → atomic}/typography.less +0 -0
  93. /package/lib/{css/atomic → atomic}/width-height.less +0 -0
  94. /package/lib/{css/base → base}/body.less +0 -0
  95. /package/lib/{css/base → base}/configuration-static.less +0 -0
  96. /package/lib/{css/base → base}/fieldset.less +0 -0
  97. /package/lib/{css/base/internals.less → base/internal.less} +0 -0
  98. /package/lib/{css/base → base}/reset-meyer.less +0 -0
  99. /package/lib/{css/base → base}/reset-normalize.less +0 -0
  100. /package/lib/{css/base → base}/reset.less +0 -0
  101. /package/lib/{css/components → components/activity-indicator}/activity-indicator.less +0 -0
  102. /package/lib/{css/components/anchors.less → components/anchor/anchor.less} +0 -0
  103. /package/lib/{css/components/avatars.less → components/avatar/avatar.less} +0 -0
  104. /package/lib/{css/components → components/award-bling}/award-bling.less +0 -0
  105. /package/lib/{css/components/badges.less → components/badge/badge.less} +0 -0
  106. /package/lib/{css/components → components/block-link}/block-link.less +0 -0
  107. /package/lib/{css/components → components/breadcrumbs}/breadcrumbs.less +0 -0
  108. /package/lib/{css/components/button-groups.less → components/button-group/button-group.less} +0 -0
  109. /package/lib/{css/components/checkboxes-radios.less → components/checkbox_radio/checkbox_radio.less} +0 -0
  110. /package/lib/{css/components/code-blocks.less → components/code-block/code-block.less} +0 -0
  111. /package/lib/{css/components → components/description}/description.less +0 -0
  112. /package/lib/{css/components/empty-states.less → components/empty-state/empty-state.less} +0 -0
  113. /package/lib/{css/components/labels.less → components/label/label.less} +0 -0
  114. /package/lib/{css/components/link-previews.less → components/link-preview/link-preview.less} +0 -0
  115. /package/lib/{css/components → components/menu}/menu.less +0 -0
  116. /package/lib/{css/components/modals.less → components/modal/modal.less} +0 -0
  117. /package/lib/{css/components → components/navigation}/navigation.less +0 -0
  118. /package/lib/{css/components/page-titles.less → components/page-title/page-title.less} +0 -0
  119. /package/lib/{css/components → components/pagination}/pagination.less +0 -0
  120. /package/lib/{css/components → components/post-summary}/post-summary.less +0 -0
  121. /package/lib/{css/components/progress-bars.less → components/progress-bar/progress-bar.less} +0 -0
  122. /package/lib/{css/components → components/prose}/prose.less +0 -0
  123. /package/lib/{css/components → components/select}/select.less +0 -0
  124. /package/lib/{css/components → components/spinner}/spinner.less +0 -0
  125. /package/lib/{css/components/tags.less → components/tag/tag.less} +0 -0
  126. /package/lib/{css/components/toggle-switches.less → components/toggle-switch/toggle-switch.less} +0 -0
  127. /package/lib/{css/components → components/topbar}/topbar.less +0 -0
  128. /package/lib/{css/components → components/uploader}/uploader.less +0 -0
  129. /package/lib/{css/components/user-cards.less → components/user-card/user-card.less} +0 -0
  130. /package/lib/{css/exports → exports}/constants-colors.less +0 -0
  131. /package/lib/{css/exports → exports}/constants-helpers.less +0 -0
  132. /package/lib/{css/exports → exports}/constants-type.less +0 -0
  133. /package/lib/{css/exports → exports}/exports.less +0 -0
  134. /package/lib/{css/exports → exports}/mixins.less +0 -0
  135. /package/lib/{css/input-utils.less → input-utils.less} +0 -0
  136. /package/lib/{css/stacks.less → stacks.less} +0 -0
  137. /package/lib/{ts/stacks.ts → stacks.ts} +0 -0
@@ -335,6 +335,24 @@
335
335
  .bc-yellow-800 { border-color: var(--yellow-800) !important; }
336
336
  .bc-yellow-900 { border-color: var(--yellow-900) !important; }
337
337
 
338
+ // $$ GOLD
339
+ // ----------------------------------------------------------------------------
340
+ .bc-gold-lighter { border-color: var(--gold-lighter) !important; }
341
+ .bc-gold { border-color: var(--gold) !important; }
342
+ .bc-gold-darker { border-color: var(--gold-darker) !important; }
343
+
344
+ // $$ SILVER
345
+ // ----------------------------------------------------------------------------
346
+ .bc-silver-lighter { border-color: var(--silver-lighter) !important; }
347
+ .bc-silver { border-color: var(--silver) !important; }
348
+ .bc-silver-darker { border-color: var(--silver-darker) !important; }
349
+
350
+ // $$ BRONZE
351
+ // ----------------------------------------------------------------------------
352
+ .bc-bronze-lighter { border-color: var(--bronze-lighter) !important; }
353
+ .bc-bronze { border-color: var(--bronze) !important; }
354
+ .bc-bronze-darker { border-color: var(--bronze-darker) !important; }
355
+
338
356
  // $$ PRIMARY
339
357
  // ----------------------------------------------------------------------------
340
358
  .bc-theme-primary-025 { border-color: var(--theme-primary-025) !important; }
@@ -1,12 +1,3 @@
1
- //
2
- // STACK OVERFLOW
3
- // ICON STYLES
4
- //
5
- // This CSS comes from Stacks, our CSS & Pattern library for rapidly building
6
- // Stack Overflow. For documentation of all these classes and how to contribute,
7
- // visit https://stackoverflow.design/
8
- //
9
-
10
1
  .svg-icon,
11
2
  .svg-spot {
12
3
  vertical-align: bottom; // Make SVG play nicely while inline with text by default
@@ -0,0 +1,21 @@
1
+ import { runComponentTests } from "../../test/test-utils";
2
+ import "../../index";
3
+
4
+ describe("activity-indicator", () => {
5
+ runComponentTests({
6
+ type: "a11y",
7
+ baseClass: "s-activity-indicator",
8
+ variants: ["danger", "success", "warning"],
9
+ children: {
10
+ default: `<div class="v-visible-sr">New activity</div>`,
11
+ new: `new<div class="v-visible-sr">New activity</div>`,
12
+ },
13
+ skippedTestids: [
14
+ "s-activity-indicator-dark-new", // TODO fix contrast issue
15
+ "s-activity-indicator-dark-success-new", // TODO fix contrast issue
16
+ "s-activity-indicator-dark-warning-new", // TODO fix contrast issue
17
+ "s-activity-indicator-light-success-new", // TODO fix contrast issue
18
+ "s-activity-indicator-light-warning-new", // TODO fix contrast issue
19
+ ],
20
+ });
21
+ });
@@ -0,0 +1,23 @@
1
+ import { html } from "@open-wc/testing";
2
+ import { runComponentTests } from "../../test/test-utils";
3
+ import "../../index";
4
+
5
+ describe("activity-indicator", () => {
6
+ runComponentTests({
7
+ type: "visual",
8
+ baseClass: "s-activity-indicator",
9
+ variants: ["danger", "success", "warning"],
10
+ children: {
11
+ default: `<div class="v-visible-sr">New activity</div>`,
12
+ new: `new<div class="v-visible-sr">New activity</div>`,
13
+ },
14
+ template: ({ component, testid }) => html`
15
+ <div
16
+ class="d-inline-flex ai-center jc-center hs1 ws1 p8"
17
+ data-testid="${testid}"
18
+ >
19
+ ${component}
20
+ </div>
21
+ `,
22
+ });
23
+ });
@@ -0,0 +1,36 @@
1
+ import { runComponentTests } from "../../test/test-utils";
2
+ import "../../index";
3
+
4
+ const getChild = (child?: string): string => {
5
+ const srEl = `<span class="v-visible-sr">Stack Overflow</span>`;
6
+ switch (child) {
7
+ case "image":
8
+ return `<img
9
+ class="s-avatar--image"
10
+ src="https://picsum.photos/id/1/48"
11
+ alt="team logo"
12
+ />${srEl}`;
13
+ case "letter":
14
+ return `<div
15
+ class="s-avatar--letter"
16
+ aria-hidden="true">
17
+ S
18
+ </div>${srEl}`;
19
+ default:
20
+ return srEl;
21
+ }
22
+ };
23
+
24
+ describe("avatar", () => {
25
+ runComponentTests({
26
+ type: "a11y",
27
+ baseClass: "s-avatar",
28
+ variants: ["24", "32", "48", "64", "96", "128"],
29
+ children: {
30
+ default: getChild(),
31
+ image: getChild("image"),
32
+ letter: getChild("letter"),
33
+ },
34
+ tag: "span",
35
+ });
36
+ });
@@ -0,0 +1,54 @@
1
+ import { html } from "@open-wc/testing";
2
+ import { runComponentTests } from "../../test/test-utils";
3
+ import "../../index";
4
+
5
+ const base64Image =
6
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAATwAAAE8CAMAAABq2/00AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABhQTFRF/3IA/////44y/8OS/+fU//Xs/6tn/9e2EhvbMQAABH9JREFUeNrs3e1y2ygAhWELJHH/d7zuJpN1XAnxoe408Ly/6xnnHXEOIEwfDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/gbAGEhrZl2VloY20PIk8tAzZbfkXA7eeuHyysVfdFMsXiY66ptiWF8Re05BdxF7HkBV7PUNW7FVx4E7sVSwrDjBw2+pC7NWwHtkTe3ULM7En9v6O2ONF7Ik9sSf2IPbEntgTexB7t8feTsynniD2Oh6tXex1hFoUe02kaxVi72JMZs+Rib0LLdkXFGLvaECWjkOxdxVmUex1rB6S2Kss2lcytSH2LnVkakPsXQ7E8xwTe5fuMrUh9vKj8CP4xN7lNOXc3mnwib1M2X7Z28VeS93mg0/sFZTG6WCMVSE51fqsYL4s9jpqQ+yV1caxErHXUxtir6c2xF5HbYRF7LXXhtjrqQ2x11MbYu9tLG4VtSH2empD7BUNxpPaSGKvPPh2sdcxX45i78baEHs18+X32hB7NfPl99oQex21MVHsxViaR8W1MU3s/XpMSvWF0tqYJfY+BmMq01dcG+scP6/6enTK9BW+1J0j9l5aYN0rP5BzM0XsbSUnAlpqY4LYezexFeRSbpslH3uDXdF64OG6est258PohXE8BK+7o6g29rJTQsPt1F3qK9qdTwMP2Wz2X1Vv0UvdbeQ53nl2Xesr2Z0Pww7Zs5lsafWW1MY+6JDNxv5/+kLb5/fXfzLirRdhKSGnr6A2tkGvJoxLGZnqva6NkAbdgA+pUN9a+QuWYYPu+98ee/W1/PJgoMG7lek7nW3UHiEdbKpcqu+kO2L9b4aG0rcWjt5jfWf2Z7kCeE891XtSG/O86A5d+tbZj1h0VW+c/ja90FG9uxMCFdWb/+SkZ1Oaq/d1vjzvXfvF+tLp+aiZT5S1Vm90lrFj02B3bW1d9X7T92u+7Aq9qk2D+Fob/v/Myu7YostC76xe9K966evfr5+7ekuXHVy1V6+R275pEMd3kPbGB+Syeod/8D7Sf41NBvPdMf7a4uXpaTGY0zd+4r0vDlKtwdPqHf/BOzzf8zQYqvRtcz54mXfUNUVyUL0T7AhcrBbKY/C36p1geVEy1y00+F3fBA9eKFxnlRXJa/XOcKxnqeF6Pv1VvTPc7RuXai6K5LN6Z9hQSUsb2Rh86pviUultaScTg/sMD15YeqmcT8/bFx1Foi/umk9P3hcMfqTenrabBT5j8DGTwHivwelOCTwNrnfJm/Ol2U0GZ74WdE+dBn9+Z/z2J9UZ7IjB7dZv8gPl9RRJIq89BiN57QZ38trn04G8k62E6xjcHuQ1D+JEXrvBnbz2+XQgr30+/SCveT69ktceg4m8doOBvDG/CXnkkTezvOXPQB555JFHHnnkkUceeeSRRx555JFHHnnkkWcnmTzyyCOPPPLII4888sgjjzzfhDzyyCOPPPL+9Ff+eyCPPPLII4888sgjjzzyyCOPPPLII4888sgDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPy//CPAAPiZOI3BPDFQAAAAAElFTkSuQmCC";
7
+
8
+ const getChild = (child?: string): string => {
9
+ const srEl = `<span class="v-visible-sr">Stack Overflow</span>`;
10
+ switch (child) {
11
+ case "image":
12
+ return `<img
13
+ class="s-avatar--image"
14
+ src="${base64Image}"
15
+ alt="team logo"
16
+ />${srEl}`;
17
+ case "letter":
18
+ return `<div
19
+ class="s-avatar--letter"
20
+ aria-hidden="true">
21
+ S
22
+ </div>${srEl}`;
23
+ default:
24
+ return srEl;
25
+ }
26
+ };
27
+
28
+ describe("avatar", () => {
29
+ runComponentTests({
30
+ type: "visual",
31
+ baseClass: "s-avatar",
32
+ variants: ["24", "32", "48", "64", "96", "128"],
33
+ children: {
34
+ default: getChild(),
35
+ image: getChild("image"),
36
+ letter: getChild("letter"),
37
+ },
38
+ attributes: {
39
+ href: "#",
40
+ },
41
+ tag: "a",
42
+ template: ({ component, testid }) => html`
43
+ <div
44
+ data-testid="${testid}"
45
+ class="d-inline-flex ai-center jc-center hmn1 wmn1 p8"
46
+ >
47
+ ${component}
48
+ </div>
49
+ `,
50
+ skippedTestids: [
51
+ /-letter/, // TODO: resolve font-family thrashing issues
52
+ ],
53
+ });
54
+ });
@@ -0,0 +1,51 @@
1
+ // See also ./lib/components/notice/notice.less
2
+ // TODO deprecate .s-banner (by turning it into a modifier on .s-notice)
3
+ // This would reduce the amount of CSS we ship to the client and simplify our codebase
4
+ .s-banner {
5
+ --_no-x-offset: 0; // [1]
6
+ .construct-notice-component(s-banner);
7
+
8
+ &[aria-hidden="true"] { // If you want to hide and reveal the banner
9
+ --_no-x-offset: calc(var(--su48) + var(--su2) * -1); // -50px
10
+ opacity: 0;
11
+ visibility: hidden;
12
+ }
13
+
14
+ &[aria-hidden="false"] {
15
+ --_no-x-offset: calc(var(--su48) + var(--su1)); // 49px
16
+ opacity: 1;
17
+ visibility: visible;
18
+ }
19
+
20
+ &.is-pinned { // If you want to put the banner above the topbar
21
+ z-index: calc(var(--zi-navigation-fixed) + 1);
22
+ }
23
+
24
+ &__body-pt {
25
+ padding-top: 93px;
26
+ }
27
+
28
+ & &--container { // When we want to keep hero content capped
29
+ margin: 0 auto;
30
+ max-width: calc(var(--s-step) * 10);
31
+ position: relative;
32
+ width: 100%;
33
+ }
34
+
35
+ border-width: var(--su-static1) 0;
36
+ inset: 0 0 auto 0;
37
+ padding: var(--su12);
38
+ position: fixed;
39
+ -webkit-transform: translate3d(0, var(--_no-x-offset), 0);
40
+ transform: translate3d(0, var(--_no-x-offset), 0);
41
+ width: 100%;
42
+ z-index: calc(var(--zi-navigation-fixed) - 1); // Tuck below topbar
43
+ }
44
+
45
+ // [1] When we use .s-banner, we need to adjust the padding-top on
46
+ // the body tag. This class correctly adjusts the body padding ONLY if
47
+ // the notice is one line. If it wraps to multiple lines, more classes or
48
+ // (ideally) JS will need to be used to determine the notice's height
49
+ // at the time of render. The padding value is determined like so:
50
+ // 50px (top bar) + 44px (notice height) - 1px (bottom border)
51
+ // The borders subtraction are necessary to neatly tuck everything together.
@@ -1,12 +1,12 @@
1
1
  import { html, fixture, expect } from "@open-wc/testing";
2
2
  import { screen } from "@testing-library/dom";
3
3
  import userEvent from "@testing-library/user-event";
4
- import "../ts/index";
5
- import { showBanner, hideBanner } from "../ts/index";
4
+ import "../../index";
5
+ import { showBanner, hideBanner } from "../../controllers";
6
6
 
7
7
  const user = userEvent.setup();
8
8
 
9
- describe("s-banner", () => {
9
+ describe("banner", () => {
10
10
  it("trigger should make banner visible", async () => {
11
11
  await fixture(html`
12
12
  <button data-toggle="s-banner" data-target="#test-banner">
@@ -29,10 +29,12 @@ describe("s-banner", () => {
29
29
  const button = screen.getByRole("button");
30
30
  const banner = screen.getByTestId("test-banner");
31
31
 
32
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
32
33
  expect(banner).to.have.attribute("aria-hidden", "true");
33
34
  button.addEventListener("click", () => showBanner(banner));
34
35
 
35
36
  await user.click(button);
37
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
36
38
  expect(banner).to.have.attribute("aria-hidden", "false");
37
39
  });
38
40
 
@@ -64,10 +66,12 @@ describe("s-banner", () => {
64
66
  const button = screen.getByRole("button");
65
67
  const banner = screen.getByTestId("test-banner");
66
68
 
69
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
67
70
  expect(banner).to.have.attribute("aria-hidden", "false");
68
71
  button.addEventListener("click", () => hideBanner(banner));
69
72
 
70
73
  await user.click(button);
74
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
71
75
  expect(banner).to.have.attribute("aria-hidden", "true");
72
76
  });
73
77
  });
@@ -1,4 +1,4 @@
1
- import * as Stacks from "../stacks";
1
+ import * as Stacks from "../../stacks";
2
2
 
3
3
  export class BannerController extends Stacks.StacksController {
4
4
  static targets = ["banner"];
@@ -0,0 +1,36 @@
1
+ import { runComponentTests } from "../../test/test-utils";
2
+ import "../../index";
3
+
4
+ const bannerChild = `
5
+ <div
6
+ class="d-flex flex__center jc-space-between s-banner--container"
7
+ role="alertdialog"
8
+ aria-describedby="banner-message"
9
+ >
10
+ <div aria-label="banner message">
11
+ Test Banner
12
+ </div>
13
+ <div class="ml-auto myn8">
14
+ <span class="s-btn s-banner--btn">Close</span>
15
+ </div>
16
+ </div>
17
+ `;
18
+
19
+ describe("banner", () => {
20
+ runComponentTests({
21
+ type: "visual",
22
+ baseClass: "s-banner",
23
+ variants: ["info", "success", "warning", "danger"],
24
+ modifiers: {
25
+ primary: ["important"],
26
+ },
27
+ attributes: {
28
+ role: "alert",
29
+ ariaHidden: "false",
30
+ },
31
+ children: {
32
+ default: bannerChild,
33
+ },
34
+ tag: "aside",
35
+ });
36
+ });
@@ -0,0 +1,32 @@
1
+ import { runComponentTests } from "../../test/test-utils";
2
+ import "../../index";
3
+
4
+ describe("button", () => {
5
+ runComponentTests({
6
+ type: "a11y",
7
+ baseClass: "s-btn",
8
+ variants: ["danger", "muted", "primary"],
9
+ modifiers: {
10
+ primary: ["filled", "outlined"],
11
+ secondary: [...["xs", "sm", "md"], ...["dropdown", "icon"]],
12
+ global: ["is-loading"],
13
+ standalone: [
14
+ ...["link", "unset"],
15
+ ...["facebook", "github", "google"],
16
+ ],
17
+ },
18
+ attributes: {
19
+ type: "button",
20
+ },
21
+ children: {
22
+ default: "Ask question",
23
+ },
24
+ tag: "button",
25
+ excludedTestids: [
26
+ /primary-outlined/, // This combination is not supported
27
+ ],
28
+ skippedTestids: [
29
+ /s-btn-dark/, // TODO remove when contrast bugs are fixed
30
+ ],
31
+ });
32
+ });
@@ -142,8 +142,6 @@
142
142
  &:focus-visible {
143
143
  outline-style: auto;
144
144
  }
145
-
146
- outline: initial;
147
145
  }
148
146
 
149
147
  &&__link {
@@ -170,6 +168,7 @@
170
168
  .s-link();
171
169
  display: inline;
172
170
  font: inherit;
171
+ outline: revert;
173
172
  text-align: inherit;
174
173
  }
175
174
 
@@ -191,6 +190,8 @@
191
190
  font: unset;
192
191
  user-select: auto;
193
192
  }
193
+
194
+ outline: initial;
194
195
  }
195
196
 
196
197
  // Pseudo-elements and child-based modifiers
@@ -0,0 +1,52 @@
1
+ import { html } from "@open-wc/testing";
2
+ import { runComponentTests } from "../../test/test-utils";
3
+ import "../../index";
4
+
5
+ const getChild = (child?: string): string => {
6
+ switch (child) {
7
+ case "badge":
8
+ return `Ask question
9
+ <span class="s-btn--badge">
10
+ <span class="s-btn--number">198</span>
11
+ </span>`;
12
+ default:
13
+ return "Ask question";
14
+ }
15
+ };
16
+
17
+ describe("button", () => {
18
+ // TODO test disabled states, interaction pseudo-classes
19
+ runComponentTests({
20
+ type: "visual",
21
+ baseClass: "s-btn",
22
+ variants: ["danger", "muted", "primary"],
23
+ modifiers: {
24
+ primary: ["filled", "outlined"],
25
+ secondary: [...["xs", "sm", "md"], ...["dropdown", "icon"]],
26
+ global: ["is-loading"],
27
+ standalone: [
28
+ ...["link", "unset"],
29
+ ...["facebook", "github", "google"],
30
+ ],
31
+ },
32
+ attributes: {
33
+ type: "button",
34
+ },
35
+ children: {
36
+ default: getChild(),
37
+ badge: getChild("badge"),
38
+ },
39
+ tag: "button",
40
+ template: ({ component, testid }) => html`
41
+ <div
42
+ class="bg-black-100 d-inline-flex ai-center jc-center hs1 ws2 p8"
43
+ data-testid="${testid}"
44
+ >
45
+ ${component}
46
+ </div>
47
+ `,
48
+ excludedTestids: [
49
+ /primary-outlined/, // This combination is not supported
50
+ ],
51
+ });
52
+ });
@@ -17,7 +17,7 @@
17
17
  }
18
18
 
19
19
  // CHILD ELEMENTS
20
- p:last-of-type {
20
+ > :last-child {
21
21
  margin-bottom: 0;
22
22
  }
23
23
 
@@ -0,0 +1,17 @@
1
+ .s-check-control { // TODO would _love_ to use .s-check instead, with no class on the input itself
2
+ --_cc-ai: center;
3
+
4
+ // CONTEXTUAL STYLES
5
+ .s-check-group & {
6
+ --_cc-ai: flex-start; // manually align the checkboxes and radios to the top of the group
7
+ }
8
+
9
+ // CHILD ELEMENTS
10
+ .s-label {
11
+ font-weight: normal;
12
+ }
13
+
14
+ align-items: var(--_cc-ai);
15
+ display: flex;
16
+ gap: var(--su8);
17
+ }
@@ -0,0 +1,19 @@
1
+ .s-check-group {
2
+ --_cg-fd: column;
3
+
4
+ // MODIFIERS
5
+ &&__horizontal {
6
+ --_cg-fd: row;
7
+ }
8
+
9
+ // CHILD ELEMENTS
10
+ // TODO HACK? <legend> isn't respecting gap...
11
+ legend.s-label {
12
+ margin-bottom: var(--su8);
13
+ }
14
+
15
+ flex-direction: var(--_cg-fd);
16
+
17
+ display: flex;
18
+ gap: var(--su8);
19
+ }
@@ -17,6 +17,7 @@
17
17
  margin-bottom @ex-transition-duration cubic-bezier(0, 0, 0, 1),
18
18
  transform @ex-transition-duration cubic-bezier(1, 0, 1, 1),
19
19
  opacity @ex-transition-duration cubic-bezier(1, 0, 1, 1);
20
+ --_ex-content-v: unset;
20
21
 
21
22
  &:not(.is-expanded) {
22
23
  --_ex-after-h: 0;
@@ -32,6 +33,7 @@
32
33
  max-height 0s @ex-transition-duration,
33
34
  transform @ex-transition-duration cubic-bezier(0, 1, 1, 1),
34
35
  opacity @ex-transition-duration cubic-bezier(0, 1, 1, 1);
36
+ --_ex-content-v: hidden;
35
37
 
36
38
  & .s-expandable--content {
37
39
  @supports ((-webkit-clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)) or (clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%))) {
@@ -62,6 +64,7 @@
62
64
  opacity: var(--_ex-content-o);
63
65
  -webkit-transform: var(--_ex-content-transform);
64
66
  transform: var(--_ex-content-transform);
67
+ visibility: var(--_ex-content-v);
65
68
 
66
69
  -ms-flex-preferred-size: 100%;
67
70
  flex-basis: 100%;
@@ -0,0 +1,53 @@
1
+ import { html, fixture, expect } from "@open-wc/testing";
2
+ import { screen } from "@testing-library/dom";
3
+ import userEvent from "@testing-library/user-event";
4
+ import "../../index";
5
+
6
+ const user = userEvent.setup();
7
+
8
+ describe("expandable-control", () => {
9
+ it("should focus on expandable content only when expanded", async () => {
10
+ const trigger = await fixture(html`
11
+ <button
12
+ data-controller="s-expandable-control"
13
+ aria-expanded="false"
14
+ aria-controls="expandable-example"
15
+ >
16
+ expandable trigger
17
+ </button>
18
+ <div class="s-expandable" id="expandable-example">
19
+ <div class="s-expandable--content">
20
+ <button>inside expandable</button>
21
+ </div>
22
+ </div>
23
+ `);
24
+
25
+ // expandable is not expanded
26
+ let expandableTrigger = screen.getByRole("button", {
27
+ name: "expandable trigger",
28
+ expanded: false,
29
+ });
30
+
31
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
32
+ expect(
33
+ screen.queryByRole("button", {
34
+ name: "inside expandable",
35
+ })
36
+ ).to.be.null;
37
+
38
+ await userEvent.click(expandableTrigger);
39
+
40
+ // expandable is now expanded
41
+ expandableTrigger = screen.getByRole("button", {
42
+ name: "expandable trigger",
43
+ expanded: true,
44
+ });
45
+
46
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
47
+ expect(
48
+ screen.getByRole("button", {
49
+ name: "inside expandable",
50
+ })
51
+ ).to.be.visible;
52
+ });
53
+ });
@@ -1,4 +1,4 @@
1
- import * as Stacks from "../stacks";
1
+ import * as Stacks from "../../stacks";
2
2
 
3
3
  // Radio buttons only trigger a change event when they're *checked*, but not when
4
4
  // they're *unchecked*. Therefore, if we have an active `s-expandable-control` in
@@ -0,0 +1,35 @@
1
+ .s-input-fill {
2
+ --_if-bc: var(--bc-darker);
3
+ --_if-bg: var(--black-050);
4
+ --_if-blw: 0;
5
+ --_if-blr: 0;
6
+ --_if-brr: 0;
7
+ --_if-brw: 0;
8
+
9
+ &&__clear {
10
+ --_if-bc: transparent;
11
+ --_if-bg: transparent;
12
+ }
13
+
14
+ &.order-first {
15
+ --_if-blw: var(--su-static1);
16
+ --_if-blr: var(--br-sm);
17
+ }
18
+
19
+ &.order-last {
20
+ --_if-brw: var(--su-static1);
21
+ --_if-brr: var(--br-sm);
22
+ }
23
+
24
+ background-color: var(--_if-bg);
25
+ border: var(--su-static1) solid var(--_if-bc);
26
+ border-left-width: var(--_if-blw);
27
+ border-right-width: var(--_if-brw);
28
+ border-radius: var(--_if-blr) var(--_if-brr) var(--_if-brr) var(--_if-blr);
29
+
30
+ color: var(--fc-medium);
31
+ font-family: inherit;
32
+ line-height: var(--lh-sm);
33
+ padding: 0.6em 0.7em;
34
+ white-space: nowrap;
35
+ }
@@ -0,0 +1,45 @@
1
+ .s-input-icon {
2
+ --_ii-fc: unset;
3
+ --_ii-r: 0.7em;
4
+
5
+ // MODIFIERS
6
+ .has-error & {
7
+ --_ii-fc: var(--red-400);
8
+ }
9
+
10
+ .has-success & {
11
+ --_ii-fc: var(--green-400);
12
+ }
13
+
14
+ .has-warning & {
15
+ --_ii-fc: var(--yellow-600);
16
+ }
17
+
18
+ .is-disabled & {
19
+ --_ii-fc: var(--black-400);
20
+ }
21
+
22
+ .is-readonly & {
23
+ --_ii-fc: var(--black-200);
24
+
25
+ .highcontrast-mode({
26
+ --_ii-fc: var(--fc-light);
27
+ });
28
+ }
29
+
30
+ &&__creditcard,
31
+ &&__search {
32
+ --_ii-r: auto;
33
+
34
+ color: var(--black-400);
35
+ left: 0.7em;
36
+ }
37
+
38
+ color: var(--_ii-fc);
39
+ right: var(--_ii-r);
40
+
41
+ margin-top: -9px; // Half the icon's height at 18px for centering;
42
+ pointer-events: none;
43
+ position: absolute;
44
+ top: 50%;
45
+ }