@stackoverflow/stacks 2.2.0 → 2.3.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.
- package/dist/css/stacks.css +2187 -758
- package/dist/css/stacks.min.css +1 -1
- package/lib/atomic/__snapshots__/spacing.less.test.ts.snap +1928 -0
- package/lib/atomic/spacing.less +59 -303
- package/lib/atomic/spacing.less.test.ts +12 -0
- package/lib/components/button/button.a11y.test.ts +14 -18
- package/lib/components/button/button.less +16 -22
- package/lib/components/button/button.test.setup.ts +36 -0
- package/lib/components/button/button.visual.test.ts +3 -33
- package/lib/components/button-group/button-group.a11y.test.ts +12 -0
- package/lib/components/button-group/button-group.less +43 -49
- package/lib/components/button-group/button-group.test.setup.ts +77 -0
- package/lib/components/button-group/button-group.visual.test.ts +7 -0
- package/lib/components/input_textarea/input_textarea.less +3 -1
- package/lib/components/post-summary/post-summary.a11y.test.ts +25 -0
- package/lib/components/post-summary/post-summary.test.setup.ts +435 -0
- package/lib/components/post-summary/post-summary.visual.test.ts +17 -0
- package/lib/components/topbar/topbar.less +365 -335
- package/lib/components/topbar/topbar.visual.test.ts +28 -0
- package/lib/exports/spacing-mixins.less +67 -0
- package/lib/tsconfig.build.json +1 -1
- package/lib/tsconfig.json +3 -3
- package/package.json +12 -12
|
@@ -2,82 +2,76 @@
|
|
|
2
2
|
// CONTEXTUAL STYLES
|
|
3
3
|
#stacks-internals #screen-sm({
|
|
4
4
|
.s-btn {
|
|
5
|
+
--_bu-px: 0.4em;
|
|
6
|
+
|
|
5
7
|
&.s-btn__dropdown {
|
|
6
8
|
padding-right: 1.2em;
|
|
7
9
|
|
|
8
10
|
&:after {
|
|
9
|
-
right:
|
|
11
|
+
right: var(--_bu-px);
|
|
10
12
|
}
|
|
11
13
|
}
|
|
12
|
-
|
|
13
|
-
padding-left: 0.4em;
|
|
14
|
-
padding-right: 0.4em;
|
|
15
14
|
}
|
|
16
15
|
}, @force-selector: true);
|
|
17
16
|
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
&:not(&--radio) .s-btn:first-child:not(:only-child),
|
|
25
|
-
&&--radio .s-btn:first-of-type:not(:last-of-type) {
|
|
26
|
-
border-top-right-radius: 0;
|
|
27
|
-
border-bottom-right-radius: 0;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
&:not(&--radio) .s-btn:last-child:not(:only-child),
|
|
31
|
-
&&--radio .s-btn:last-of-type:not(:first-of-type) {
|
|
32
|
-
border-top-left-radius: 0;
|
|
33
|
-
border-bottom-left-radius: 0;
|
|
17
|
+
// CHILD ELEMENTS
|
|
18
|
+
form {
|
|
19
|
+
display: flex;
|
|
20
|
+
margin-right: calc(var(--su-static1) * -1); // -1px
|
|
34
21
|
}
|
|
35
22
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
23
|
+
.s-btn {
|
|
24
|
+
--_bu-br: var(--br-sm);
|
|
25
|
+
--_bu-bc-hover: transparent;
|
|
40
26
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
border-radius: 0;
|
|
46
|
-
}
|
|
27
|
+
&,
|
|
28
|
+
&.s-btn__md {
|
|
29
|
+
--_bu-px: var(--su12);
|
|
30
|
+
--_bu-py: var(--su8);
|
|
47
31
|
}
|
|
48
32
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
33
|
+
&.s-btn___xs {
|
|
34
|
+
--_bu-px: var(--su8);
|
|
35
|
+
--_bu-py: var(--su4);
|
|
53
36
|
}
|
|
54
37
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
38
|
+
&.s-btn___sm {
|
|
39
|
+
--_bu-px: calc(var(--su8) + var(--su2));
|
|
40
|
+
--_bu-py: var(--su6);
|
|
59
41
|
}
|
|
60
42
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
43
|
+
&.is-selected,
|
|
44
|
+
&--radio:checked + .s-btn {
|
|
45
|
+
font-weight: bold;
|
|
46
|
+
}
|
|
64
47
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
z-index: var(--zi-active);
|
|
48
|
+
.s-btn--badge {
|
|
49
|
+
font-weight: normal;
|
|
68
50
|
}
|
|
69
51
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
52
|
+
& .s-btn--text {
|
|
53
|
+
&:before {
|
|
54
|
+
content: attr(data-text);
|
|
55
|
+
content: attr(data-text) / "";
|
|
56
|
+
font-weight: bold;
|
|
57
|
+
height: 0;
|
|
58
|
+
pointer-events: none;
|
|
59
|
+
user-select: none;
|
|
60
|
+
visibility: hidden;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
display: inline-flex;
|
|
64
|
+
flex-direction: column;
|
|
73
65
|
}
|
|
74
66
|
|
|
75
|
-
margin-bottom: calc(var(--su-static1) * -1); // When wrapping we need to account for the border
|
|
76
67
|
white-space: nowrap; // When the buttons wrap, they get super tall and mess up the whole layout
|
|
77
68
|
}
|
|
78
69
|
|
|
79
70
|
// STATIC COMPONENT STYLES
|
|
80
|
-
|
|
71
|
+
border: var(--su-static1) solid var(--black-300);
|
|
72
|
+
border-radius: var(--br-md);
|
|
73
|
+
display: inline-flex; // TODO investigate if changing from flex to inline-flex will be an issue
|
|
81
74
|
flex-wrap: wrap;
|
|
82
75
|
margin-bottom: var(--su-static1); // Compensate for buttons having a margin bottom of -1px to account for row wrapping
|
|
76
|
+
padding: calc(var(--su-static4) - var(--su-static1));
|
|
83
77
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { html } from "@open-wc/testing";
|
|
2
|
+
import type { TestVariationArgs } from "../../test/test-utils";
|
|
3
|
+
|
|
4
|
+
const btns = [
|
|
5
|
+
{ name: "Newest", isSelected: true },
|
|
6
|
+
{ name: "Frequent" },
|
|
7
|
+
{ name: "Active" },
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
const getBtn = ({
|
|
11
|
+
name = "",
|
|
12
|
+
isRadio,
|
|
13
|
+
isSelected,
|
|
14
|
+
hasBadge,
|
|
15
|
+
}: {
|
|
16
|
+
name: string;
|
|
17
|
+
isRadio?: boolean;
|
|
18
|
+
isSelected?: boolean;
|
|
19
|
+
hasBadge?: boolean;
|
|
20
|
+
}): string => {
|
|
21
|
+
const baseClasses = "s-btn s-btn__muted";
|
|
22
|
+
const btnChildren = `
|
|
23
|
+
<span class="s-btn--text" data-text="${name}">${name}</span>
|
|
24
|
+
${
|
|
25
|
+
hasBadge
|
|
26
|
+
? `<span class="s-btn--badge"><span class="s-btn--number">123</span></span>`
|
|
27
|
+
: ""
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
return isRadio
|
|
32
|
+
? `<input
|
|
33
|
+
class="s-btn--radio"
|
|
34
|
+
type="radio"
|
|
35
|
+
name="test-btn-radio-group"
|
|
36
|
+
id="btn-${name}"
|
|
37
|
+
${isSelected ? "checked" : ""}/>
|
|
38
|
+
<label class="${baseClasses}" for="btn-${name}">
|
|
39
|
+
${btnChildren}
|
|
40
|
+
</label>`
|
|
41
|
+
: `<button
|
|
42
|
+
class="${baseClasses}${isSelected ? " is-selected" : ""}"
|
|
43
|
+
${isSelected ? `aria-current="true"` : ""}
|
|
44
|
+
type="button">
|
|
45
|
+
${btnChildren}
|
|
46
|
+
</button>`;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const getBtns = (ids: number[]): string => {
|
|
50
|
+
return ids.map((id) => getBtn(btns[id])).join("");
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const testArgs: TestVariationArgs = {
|
|
54
|
+
baseClass: "s-btn-group",
|
|
55
|
+
children: {
|
|
56
|
+
default: getBtns([0, 1, 2]),
|
|
57
|
+
single: getBtns([0]),
|
|
58
|
+
form: `
|
|
59
|
+
${getBtns([0])}
|
|
60
|
+
<form class="mb0 p0">
|
|
61
|
+
${getBtn(btns[1])}
|
|
62
|
+
</form>
|
|
63
|
+
${getBtns([2])}
|
|
64
|
+
`,
|
|
65
|
+
badge: btns.map((btn) => getBtn({ ...btn, hasBadge: true })).join(""),
|
|
66
|
+
radio: btns.map((btn) => getBtn({ ...btn, isRadio: true })).join(""),
|
|
67
|
+
},
|
|
68
|
+
template: ({ component, testid }) =>
|
|
69
|
+
html`<div
|
|
70
|
+
class="d-inline-flex ai-center jc-center p8"
|
|
71
|
+
data-testid="${testid}"
|
|
72
|
+
>
|
|
73
|
+
${component}
|
|
74
|
+
</div>`,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export { testArgs };
|
|
@@ -102,7 +102,9 @@
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
// INTERACTION
|
|
105
|
-
|
|
105
|
+
// Note: We're applying the focus styles both on `:focus` and `:focus-within` since this component can sometimes be used on the parent of an input such as in our tag selector in Core.
|
|
106
|
+
&:focus,
|
|
107
|
+
&:focus-within {
|
|
106
108
|
.focus-styles();
|
|
107
109
|
}
|
|
108
110
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { runA11yTests } from "../../test/a11y-test-utils";
|
|
2
|
+
import testArgs from "./post-summary.test.setup";
|
|
3
|
+
import "../../index";
|
|
4
|
+
|
|
5
|
+
describe("post-summary", () => {
|
|
6
|
+
// Base, sparce
|
|
7
|
+
runA11yTests({
|
|
8
|
+
...testArgs.base,
|
|
9
|
+
// TODO resolve test failures
|
|
10
|
+
skippedTestids: [
|
|
11
|
+
/-deleted/,
|
|
12
|
+
/-ignored/,
|
|
13
|
+
/-highcontrast-(light|dark)-watched/,
|
|
14
|
+
],
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Truncated description sizes
|
|
18
|
+
runA11yTests(testArgs.sizes);
|
|
19
|
+
|
|
20
|
+
// Stats - answers, view hotness
|
|
21
|
+
runA11yTests(testArgs.stats);
|
|
22
|
+
|
|
23
|
+
// Badges
|
|
24
|
+
runA11yTests(testArgs.badges);
|
|
25
|
+
});
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
import { html } from "@open-wc/testing";
|
|
2
|
+
import {
|
|
3
|
+
IconArchiveSm,
|
|
4
|
+
IconCheckmarkSm,
|
|
5
|
+
IconEllipsisVertical,
|
|
6
|
+
IconEyeSm,
|
|
7
|
+
IconNotInterested,
|
|
8
|
+
IconPencilSm,
|
|
9
|
+
IconTackSm,
|
|
10
|
+
IconTrashSm,
|
|
11
|
+
} from "@stackoverflow/stacks-icons/icons";
|
|
12
|
+
import type { TestVariationArgs } from "../../test/test-utils";
|
|
13
|
+
import "../../index";
|
|
14
|
+
|
|
15
|
+
type BadgeType =
|
|
16
|
+
| "danger"
|
|
17
|
+
| "danger-filled"
|
|
18
|
+
| "info"
|
|
19
|
+
| "muted"
|
|
20
|
+
| "muted-filled"
|
|
21
|
+
| "warning";
|
|
22
|
+
|
|
23
|
+
type Stats = {
|
|
24
|
+
votes: number;
|
|
25
|
+
views: number;
|
|
26
|
+
answers: number;
|
|
27
|
+
accepted?: boolean;
|
|
28
|
+
bounty?: number;
|
|
29
|
+
badge?: BadgeType;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type Tags = { text: string; type?: "required" | "moderator" }[];
|
|
33
|
+
|
|
34
|
+
type TruncationSizes = "sm" | "md" | "lg" | "";
|
|
35
|
+
|
|
36
|
+
const formatNumber = (num: number) => {
|
|
37
|
+
switch (true) {
|
|
38
|
+
case num > 10000090:
|
|
39
|
+
return (num / 1000000).toFixed(0) + "m";
|
|
40
|
+
case num > 1000000:
|
|
41
|
+
return (num / 1000000).toFixed(1) + "m";
|
|
42
|
+
case num > 10000:
|
|
43
|
+
return (num / 1000).toFixed(0) + "k";
|
|
44
|
+
case num > 1000:
|
|
45
|
+
return (num / 1000).toFixed(1) + "k";
|
|
46
|
+
default:
|
|
47
|
+
return num.toString();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const getBadge = (type: BadgeType) => {
|
|
52
|
+
const badgeClasses = type
|
|
53
|
+
.split("-")
|
|
54
|
+
.map((modifier) => `s-badge__${modifier}`)
|
|
55
|
+
.join(" ");
|
|
56
|
+
const getIcon = () => {
|
|
57
|
+
switch (type) {
|
|
58
|
+
case "danger":
|
|
59
|
+
return IconNotInterested;
|
|
60
|
+
case "danger-filled":
|
|
61
|
+
return IconTrashSm;
|
|
62
|
+
case "info":
|
|
63
|
+
return IconPencilSm;
|
|
64
|
+
case "muted":
|
|
65
|
+
return IconArchiveSm;
|
|
66
|
+
case "muted-filled":
|
|
67
|
+
return IconTackSm;
|
|
68
|
+
case "warning":
|
|
69
|
+
return IconEyeSm;
|
|
70
|
+
default:
|
|
71
|
+
return "";
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const getText = () => {
|
|
76
|
+
switch (type) {
|
|
77
|
+
case "danger":
|
|
78
|
+
return "Closed";
|
|
79
|
+
case "danger-filled":
|
|
80
|
+
return "Deleted";
|
|
81
|
+
case "info":
|
|
82
|
+
return "Draft";
|
|
83
|
+
case "muted":
|
|
84
|
+
return "Archived";
|
|
85
|
+
case "muted-filled":
|
|
86
|
+
return "Pinned";
|
|
87
|
+
case "warning":
|
|
88
|
+
return "Review";
|
|
89
|
+
default:
|
|
90
|
+
return "";
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
return `
|
|
95
|
+
<div
|
|
96
|
+
class="s-post-summary--stats-item s-badge s-badge__icon ${badgeClasses}"
|
|
97
|
+
>
|
|
98
|
+
${getIcon()}
|
|
99
|
+
${getText()}
|
|
100
|
+
</div>`;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const getDescription = (truncation?: TruncationSizes, text?: string) => `
|
|
104
|
+
<p class="s-post-summary--content-excerpt
|
|
105
|
+
${truncation ? `s-post-summary--content-excerpt__${truncation}` : ""}
|
|
106
|
+
">
|
|
107
|
+
${text ? text : "In the spirit of type safety associated with the CriteriaQuery JPA 2.0 also has an API to support Metamodel representation of entities. Is anyone aware of a fully functional implementation of this API (to generate the Metamodel as opposed to creating the metamodel classes manually)? It would be awesome if someone also knows the steps for setting this up in Eclipse (I assume it's as simple as setting up an annotation processor, but you never know)."}
|
|
108
|
+
</p>
|
|
109
|
+
`;
|
|
110
|
+
|
|
111
|
+
const getHotnessClass = (num: number) => {
|
|
112
|
+
switch (true) {
|
|
113
|
+
case num > 100000:
|
|
114
|
+
return "is-supernova";
|
|
115
|
+
case num > 10000:
|
|
116
|
+
return "is-hot";
|
|
117
|
+
case num > 1000:
|
|
118
|
+
return "is-warm";
|
|
119
|
+
default:
|
|
120
|
+
return "";
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const getStats = ({
|
|
125
|
+
votes,
|
|
126
|
+
views,
|
|
127
|
+
answers,
|
|
128
|
+
accepted,
|
|
129
|
+
bounty,
|
|
130
|
+
badge,
|
|
131
|
+
}: Stats) => `
|
|
132
|
+
<div class="s-post-summary--stats">
|
|
133
|
+
${badge ? getBadge(badge) : ""}
|
|
134
|
+
<div class="s-post-summary--stats-item s-post-summary--stats-item__emphasized">
|
|
135
|
+
<span class="s-post-summary--stats-item-number">${formatNumber(votes)}</span>
|
|
136
|
+
<span class="s-post-summary--stats-item-unit">${votes === 1 ? "vote" : "votes"}</span>
|
|
137
|
+
</div>
|
|
138
|
+
<div class="s-post-summary--stats-item ${accepted ? "has-accepted-answer" : ""} ${answers > 0 ? "has-answers" : ""}">
|
|
139
|
+
${accepted ? IconCheckmarkSm : ""}
|
|
140
|
+
<span class="s-post-summary--stats-item-number">${formatNumber(answers)}</span>
|
|
141
|
+
<span class="s-post-summary--stats-item-unit">${answers === 1 ? "answer" : "answers"}</span>
|
|
142
|
+
</div>
|
|
143
|
+
<div class="s-post-summary--stats-item ${getHotnessClass(views)}">
|
|
144
|
+
<span class="s-post-summary--stats-item-number">${formatNumber(views)}</span>
|
|
145
|
+
<span class="s-post-summary--stats-item-unit">${views === 1 ? "view" : "views"}</span>
|
|
146
|
+
</div>
|
|
147
|
+
${
|
|
148
|
+
bounty
|
|
149
|
+
? `
|
|
150
|
+
<div class="s-post-summary--stats-item s-badge s-badge__bounty">
|
|
151
|
+
+${bounty}
|
|
152
|
+
</div>
|
|
153
|
+
`
|
|
154
|
+
: ""
|
|
155
|
+
}
|
|
156
|
+
</div>
|
|
157
|
+
`;
|
|
158
|
+
|
|
159
|
+
const getTags = (tags?: Tags) => {
|
|
160
|
+
const tagsArr = tags ?? [
|
|
161
|
+
{
|
|
162
|
+
text: "feature-request",
|
|
163
|
+
type: "required",
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
text: "status-complete",
|
|
167
|
+
type: "moderator",
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
text: "design",
|
|
171
|
+
},
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
const tagsHTML = tagsArr
|
|
175
|
+
.map(
|
|
176
|
+
({ text, type }) => `
|
|
177
|
+
<a class="s-tag ${type ? `s-tag__${type}` : ""}" href="/">${text}</a>
|
|
178
|
+
`
|
|
179
|
+
)
|
|
180
|
+
.join("");
|
|
181
|
+
|
|
182
|
+
return `<div class="s-post-summary--meta-tags">${tagsHTML}</div>`;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const getUser = () => `
|
|
186
|
+
<div class="s-user-card s-user-card__minimal">
|
|
187
|
+
<a href="#" class="s-avatar s-user-card--avatar">
|
|
188
|
+
<img class="s-avatar--image" src="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" alt="placeholder avatar">
|
|
189
|
+
<span class="v-visible-sr">Tracy Smith</span>
|
|
190
|
+
</a>
|
|
191
|
+
<a href="#" class="s-user-card--link">Tracy Smith</a>
|
|
192
|
+
<ul class="s-user-card--awards">
|
|
193
|
+
<li class="s-user-card--rep">1350</li>
|
|
194
|
+
</ul>
|
|
195
|
+
<time class="s-user-card--time">asked just now</time>
|
|
196
|
+
</div>
|
|
197
|
+
`;
|
|
198
|
+
|
|
199
|
+
const getChildren = ({
|
|
200
|
+
show = {
|
|
201
|
+
description: false,
|
|
202
|
+
menuBtn: false,
|
|
203
|
+
stats: false,
|
|
204
|
+
tags: false,
|
|
205
|
+
title: false,
|
|
206
|
+
user: false,
|
|
207
|
+
},
|
|
208
|
+
description = {
|
|
209
|
+
truncation: "",
|
|
210
|
+
text: "",
|
|
211
|
+
},
|
|
212
|
+
stats,
|
|
213
|
+
tags,
|
|
214
|
+
title,
|
|
215
|
+
}: {
|
|
216
|
+
show?: {
|
|
217
|
+
description?: boolean;
|
|
218
|
+
menuBtn?: boolean;
|
|
219
|
+
stats?: boolean;
|
|
220
|
+
tags?: boolean;
|
|
221
|
+
title?: boolean;
|
|
222
|
+
user?: boolean;
|
|
223
|
+
};
|
|
224
|
+
description?: {
|
|
225
|
+
truncation?: TruncationSizes;
|
|
226
|
+
text?: string;
|
|
227
|
+
};
|
|
228
|
+
stats?: Stats;
|
|
229
|
+
tags?: Tags;
|
|
230
|
+
title?: string;
|
|
231
|
+
}) => {
|
|
232
|
+
const titleEl =
|
|
233
|
+
show.title || title
|
|
234
|
+
? `
|
|
235
|
+
<h3 class="s-post-summary--content-title">
|
|
236
|
+
${title ? title : "How to generate the JPA entity Metamodel?"}
|
|
237
|
+
</h3>
|
|
238
|
+
`
|
|
239
|
+
: "";
|
|
240
|
+
const descriptionEl =
|
|
241
|
+
show.description || description.truncation || description.text
|
|
242
|
+
? description
|
|
243
|
+
? getDescription(description.truncation, description.text)
|
|
244
|
+
: getDescription()
|
|
245
|
+
: "";
|
|
246
|
+
const tagsEl = show.tags || tags ? getTags(tags) : "";
|
|
247
|
+
const userEl = show.user ? getUser() : "";
|
|
248
|
+
const menuBtnEl = show.menuBtn
|
|
249
|
+
? `
|
|
250
|
+
<a href="#" class="s-btn s-btn__muted s-post-summary--content-menu-button">
|
|
251
|
+
${IconEllipsisVertical}
|
|
252
|
+
<span class="v-visible-sr">menu</span>
|
|
253
|
+
</a>
|
|
254
|
+
`
|
|
255
|
+
: "";
|
|
256
|
+
|
|
257
|
+
return `
|
|
258
|
+
${
|
|
259
|
+
show.stats || stats
|
|
260
|
+
? getStats(
|
|
261
|
+
stats
|
|
262
|
+
? stats
|
|
263
|
+
: {
|
|
264
|
+
votes: 95,
|
|
265
|
+
views: 104123,
|
|
266
|
+
answers: 5,
|
|
267
|
+
accepted: true,
|
|
268
|
+
bounty: 50,
|
|
269
|
+
}
|
|
270
|
+
)
|
|
271
|
+
: ""
|
|
272
|
+
}
|
|
273
|
+
${
|
|
274
|
+
titleEl || descriptionEl || tagsEl || userEl || menuBtnEl
|
|
275
|
+
? `
|
|
276
|
+
<div class="s-post-summary--content">
|
|
277
|
+
${titleEl}
|
|
278
|
+
${descriptionEl}
|
|
279
|
+
<div class="s-post-summary--meta">
|
|
280
|
+
${tagsEl}
|
|
281
|
+
${userEl}
|
|
282
|
+
</div>
|
|
283
|
+
${menuBtnEl}
|
|
284
|
+
</div>`
|
|
285
|
+
: ""
|
|
286
|
+
}
|
|
287
|
+
`;
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const getBadgeChildren = (badge: BadgeType) => {
|
|
291
|
+
return getChildren({
|
|
292
|
+
show: {
|
|
293
|
+
stats: true,
|
|
294
|
+
},
|
|
295
|
+
stats: {
|
|
296
|
+
badge: badge as BadgeType,
|
|
297
|
+
answers: 0,
|
|
298
|
+
votes: 1,
|
|
299
|
+
views: 20,
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
const getSizeChildren = (size: string) => {
|
|
305
|
+
return getChildren({
|
|
306
|
+
show: {
|
|
307
|
+
description: true,
|
|
308
|
+
menuBtn: true,
|
|
309
|
+
stats: true,
|
|
310
|
+
tags: true,
|
|
311
|
+
title: true,
|
|
312
|
+
user: true,
|
|
313
|
+
},
|
|
314
|
+
description: {
|
|
315
|
+
truncation: size as TruncationSizes,
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
const getStatsChildren = ({
|
|
321
|
+
accepted = false,
|
|
322
|
+
answers = 1,
|
|
323
|
+
views = 20,
|
|
324
|
+
}: {
|
|
325
|
+
accepted?: boolean;
|
|
326
|
+
answers?: number;
|
|
327
|
+
views?: number;
|
|
328
|
+
}) =>
|
|
329
|
+
getChildren({
|
|
330
|
+
show: {
|
|
331
|
+
stats: true,
|
|
332
|
+
},
|
|
333
|
+
stats: {
|
|
334
|
+
votes: 1,
|
|
335
|
+
answers,
|
|
336
|
+
accepted,
|
|
337
|
+
views,
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
342
|
+
const template = ({ component, testid }: any) => html`
|
|
343
|
+
<div class="d-flex ai-center jc-center p8 ws6" data-testid="${testid}">
|
|
344
|
+
${component}
|
|
345
|
+
</div>
|
|
346
|
+
`;
|
|
347
|
+
|
|
348
|
+
const testArgs: {
|
|
349
|
+
[key: string]: TestVariationArgs;
|
|
350
|
+
} = {
|
|
351
|
+
base: {
|
|
352
|
+
baseClass: "s-post-summary",
|
|
353
|
+
modifiers: {
|
|
354
|
+
primary: ["deleted", "ignored", "watched"], // variants described as modifiers to test colliding modifiers
|
|
355
|
+
secondary: ["minimal"],
|
|
356
|
+
global: ["w100"],
|
|
357
|
+
},
|
|
358
|
+
children: {
|
|
359
|
+
default: getChildren({
|
|
360
|
+
show: {
|
|
361
|
+
description: true,
|
|
362
|
+
menuBtn: true,
|
|
363
|
+
stats: true,
|
|
364
|
+
tags: true,
|
|
365
|
+
title: true,
|
|
366
|
+
user: true,
|
|
367
|
+
},
|
|
368
|
+
}),
|
|
369
|
+
sparce: getChildren({
|
|
370
|
+
show: {
|
|
371
|
+
stats: true,
|
|
372
|
+
tags: true,
|
|
373
|
+
title: true,
|
|
374
|
+
user: true,
|
|
375
|
+
},
|
|
376
|
+
tags: [
|
|
377
|
+
{
|
|
378
|
+
text: "featured-request",
|
|
379
|
+
type: "required",
|
|
380
|
+
},
|
|
381
|
+
],
|
|
382
|
+
title: "Short title",
|
|
383
|
+
}),
|
|
384
|
+
},
|
|
385
|
+
options: {
|
|
386
|
+
includeNullModifier: false,
|
|
387
|
+
},
|
|
388
|
+
template,
|
|
389
|
+
},
|
|
390
|
+
badges: {
|
|
391
|
+
baseClass: "s-post-summary",
|
|
392
|
+
children: {
|
|
393
|
+
"badge-danger": getBadgeChildren("danger"),
|
|
394
|
+
"badge-danger-filled": getBadgeChildren("danger-filled"),
|
|
395
|
+
"badge-info": getBadgeChildren("info"),
|
|
396
|
+
"badge-muted": getBadgeChildren("muted"),
|
|
397
|
+
"badge-muted-filled": getBadgeChildren("muted-filled"),
|
|
398
|
+
"badge-warning": getBadgeChildren("warning"),
|
|
399
|
+
},
|
|
400
|
+
template,
|
|
401
|
+
},
|
|
402
|
+
sizes: {
|
|
403
|
+
baseClass: "s-post-summary",
|
|
404
|
+
modifiers: {
|
|
405
|
+
global: ["w100"],
|
|
406
|
+
},
|
|
407
|
+
children: {
|
|
408
|
+
"description-sm": getSizeChildren("sm"),
|
|
409
|
+
"description-md": getSizeChildren("md"),
|
|
410
|
+
"description-lg": getSizeChildren("lg"),
|
|
411
|
+
},
|
|
412
|
+
options: {
|
|
413
|
+
includeNullModifier: false,
|
|
414
|
+
},
|
|
415
|
+
template,
|
|
416
|
+
},
|
|
417
|
+
stats: {
|
|
418
|
+
baseClass: "s-post-summary",
|
|
419
|
+
children: {
|
|
420
|
+
"stats-unanswered": getStatsChildren({ answers: 0 }),
|
|
421
|
+
"stats-answered": getStatsChildren({ answers: 1 }),
|
|
422
|
+
"stats-answered-accepted": getStatsChildren({
|
|
423
|
+
answers: 10,
|
|
424
|
+
accepted: true,
|
|
425
|
+
}),
|
|
426
|
+
"stats-views": getStatsChildren({ views: 1 }),
|
|
427
|
+
"stats-views-warm": getStatsChildren({ views: 1001 }),
|
|
428
|
+
"stats-views-hot": getStatsChildren({ views: 10001 }),
|
|
429
|
+
"stats-views-supernova": getStatsChildren({ views: 100001 }),
|
|
430
|
+
},
|
|
431
|
+
template,
|
|
432
|
+
},
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
export default testArgs;
|