Skip to content

Commit 1014f22

Browse files
castastrophekylebuch8
authored andcommitted
Pt 2: Storybook updates (#277)
* Update pfe-accordion and pfe-card to dynamic approach * Add cta and tabs compatibility with new approach * Remove commented out code; optimize rendering with a shared util function * Update tabs with orientation fix * Fix fallback for booleans and update datetime to show knobs * Update remaining storybook issues * Fix issue with the default not being injected * Updates based on feedback re: kyle * Add manual clean of empty boolean attributes * Update story rendering * Update band and tab story to fix dropdowns and tab rendering * Clean up inconsistencies after merge * Clean up schemas a bit for consistency * Fix typo in accordion * Remove deprecated prefix value from util function * Remove striped test since that attribute is no longer supported
1 parent fac8738 commit 1014f22

File tree

18 files changed

+530
-369
lines changed

18 files changed

+530
-369
lines changed

.storybook/utils.js

Lines changed: 75 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// Automatic content generation
44
// https://www.npmjs.com/package/lorem-ipsum
55
const loremIpsum = require("lorem-ipsum");
6+
// HTML cleaner
7+
// https://www.npmjs.com/package/clean-html
68
const cleaner = require("clean-html");
79

810
// Escape HTML to display markup as content
@@ -19,27 +21,56 @@ String.prototype.sentenceCase = function() {
1921
};
2022

2123
// Print attributes based on an object
22-
const listProperties = (obj, prefix = "") =>
24+
const listProperties = (obj) =>
2325
Object.entries(obj)
2426
.map(set => {
27+
let string = " ";
2528
let p = set[0];
2629
let v = set[1];
27-
let print = set[2] || true;
28-
return print && v && v !== "null"
29-
? ` ${p !== "slot" ? `${prefix ? `${prefix}-` : ""}` : ""}${p}="${v}"`
30-
: "";
30+
let print = set[2];
31+
32+
// If no print value is provided, default to true
33+
if (typeof print === "undefined") {
34+
print = true;
35+
}
36+
37+
// If printing is allowed, the value exists and is not null and is not a slot
38+
if (
39+
print &&
40+
typeof v !== "undefined" &&
41+
(v !== null && v !== "null") &&
42+
p !== "slot"
43+
) {
44+
string += p;
45+
// If the value is a boolean and is false, or the value is not a string true
46+
if (
47+
(typeof v === "string" && v !== "true") ||
48+
(typeof v === "boolean" && !v)
49+
) {
50+
string += "=";
51+
if (typeof v === "string") {
52+
// If it's a string, use quotation marks around it
53+
string += `"${v}"`;
54+
} else {
55+
// Use, use it raw
56+
string += v;
57+
}
58+
}
59+
}
60+
return string.toLowerCase();
3161
})
32-
.join("");
62+
.join(" ");
3363

3464
// Create a tag based on a provided object
3565
// Accepts an object that can contain (all optional):
3666
// -- tag: html tag such as h1 or p, default is div
3767
// -- slot: rendered as slot="<input>"
3868
// -- attributes: passed through the listProperties function
3969
// -- content: Accepts html or plain text or renders default content
40-
export function customTag(obj, prefix = "") {
70+
export function customTag(obj) {
4171
let start = "";
4272
let end = "";
73+
4374
// If a tag is defined, or it has slots or attributes to apply
4475
// render an open and close tag
4576
if (obj.tag || obj.slot || obj.attributes) {
@@ -54,11 +85,11 @@ export function customTag(obj, prefix = "") {
5485
end += "div";
5586
}
5687
start += obj.slot ? ` slot="${obj.slot}"` : "";
57-
start += obj.attributes ? listProperties(obj.attributes || {}, prefix) : "";
88+
start += obj.attributes ? listProperties(obj.attributes || {}) : "";
5889
start += ">";
5990
end += ">";
6091
}
61-
return `${start}${obj.content || autoContent()}${end}`;
92+
return `${start}${obj.content}${end}`;
6293
}
6394

6495
const parseMarkup = string => {
@@ -133,15 +164,15 @@ const renderSlots = (slots = []) =>
133164
.join("");
134165

135166
// Creates a component dynamically based on inputs
136-
export function component(tag, attributes = {}, slots = [], prefix = "") {
137-
return `<${tag}${listProperties(attributes, prefix)}>${
167+
export function component(tag, attributes = {}, slots = []) {
168+
return `<${tag}${listProperties(attributes)}>${
138169
slots.length > 0 ? renderSlots(slots) : autoContent()
139170
}</${tag}>`;
140171
}
141172

142173
// Create an automatic heading
143174
export function autoHeading(short = false) {
144-
let length = short ? Math.random() + 2 : Math.random() * 10 + 5;
175+
let length = short ? Math.random() + 1 : Math.random() * 10 + 5;
145176
return loremIpsum({
146177
count: length,
147178
units: "words"
@@ -168,25 +199,52 @@ export function autoPropKnobs(properties, bridge) {
168199
let type = prop[1].type || "string";
169200
let defaultValue = prop[1].default;
170201
let options = prop[1].enum || [];
171-
let hidden = prop[1].hidden || false;
202+
let hidden = prop[1].hidden;
203+
let required = prop[1].required;
204+
let prefixed = prop[1].prefixed;
205+
206+
// Convert the type to lowercase values
207+
type = type.toLowerCase();
208+
209+
// Initialize booleans to false if undefined
210+
if (typeof hidden === "undefined") {
211+
hidden = false;
212+
}
213+
214+
if (typeof required === "undefined") {
215+
required = false;
216+
}
217+
218+
if (typeof prefixed === "undefined") {
219+
prefixed = false;
220+
}
221+
222+
if(prefixed) {
223+
attr = `pfe-${attr}`;
224+
}
172225

173226
// Set the default method to text
174227
let method = "text";
175228
if (["boolean", "number", "object", "array", "date"].includes(type)) {
176-
method = type.toLowerCase();
229+
method = type;
177230
}
178231

179232
// If the property is not hidden from the user
180233
if (!hidden) {
181234
// If an array of options exists, create a select list
182235
if (options.length > 0) {
183236
let opts = {};
237+
238+
// If this is not a required field, add a null option
239+
if (!required) {
240+
opts.null = "-- Not selected --";
241+
}
242+
184243
// Convert the array into an object
185244
options.map(item => (opts[item] = item));
186245

187-
// If a default is not defined, add a null option
246+
// If the default value is not defined, use the new null option as the default
188247
if (defaultValue === "" || defaultValue === null) {
189-
opts.null = "none";
190248
defaultValue = null;
191249
}
192250

@@ -258,7 +316,7 @@ export function code(markup) {
258316

259317
// Return the rendered markup and the code snippet output
260318
return `<pre style="white-space: pre-wrap; padding: 20px 50px; background-color: #f0f0f0; font-weight: bold; border: 1px solid #bccc;">${escapeHTML(
261-
markup
319+
markup.replace(/\=\"\"/g, "")
262320
)}</pre>`;
263321
}
264322
// prettier-ignore-end

elements/pfe-accordion/demo/pfe-accordion.story.js

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ import PfeAccordion from "../pfe-accordion";
66

77
const stories = storiesOf("Accordion", module);
88

9-
const defaultHeader = tools.autoHeading();
10-
const defaultContent = tools.autoContent(5, 3);
11-
129
// Define the template to be used
1310
const template = (data = {}) => {
1411
return tools.component(PfeAccordion.tag, data.prop, data.slots);
@@ -18,8 +15,10 @@ stories.addDecorator(storybookBridge.withKnobs);
1815

1916
stories.add(PfeAccordion.tag, () => {
2017
let config = {};
18+
let headings = [];
19+
let panels = [];
20+
2121
// const props = PfeAccordion.properties;
22-
// const slots = PfeAccordion.slots;
2322
const props = {
2423
on: {
2524
title: "Theme",
@@ -28,13 +27,15 @@ stories.add(PfeAccordion.tag, () => {
2827
"light",
2928
"dark"
3029
],
31-
default: "light"
30+
default: "light",
31+
prefixed: false
3232
}
3333
};
3434

3535
config.prop = tools.autoPropKnobs(props, storybookBridge);
3636

3737
//-- Add content to light DOM
38+
// const slots = PfeAccordion.slots;
3839
config.slots = [];
3940

4041
// Let the user determine number of accordions
@@ -43,42 +44,36 @@ stories.add(PfeAccordion.tag, () => {
4344
max: 10
4445
});
4546

46-
// Let the user customize the first header + panel set
47-
let header = storybookBridge.text("Header", defaultHeader, "accordion-set");
48-
let content = storybookBridge.text("Panel", defaultContent, "accordion-set");
49-
50-
config.slots.push({
51-
content:
52-
tools.component("pfe-accordion-header", {}, [
53-
{
54-
content: tools.customTag({
55-
tag: "h2",
56-
content: header
57-
})
58-
}
59-
]) +
60-
tools.component("pfe-accordion-panel", {}, [
61-
{
62-
content: content
63-
}
64-
])
65-
});
47+
// Ask user if they want to add any custom content
48+
const customContent = storybookBridge.boolean(
49+
"Use custom content?",
50+
false,
51+
"Content"
52+
);
53+
54+
// Let the user customize the header + panel set
55+
if (customContent) {
56+
for (let i = 0; i < accordionCount; i++) {
57+
headings[i] = storybookBridge.text(`Heading ${i + 1}`, "", "accordion-set");
58+
panels[i] = storybookBridge.text(`Panel ${i + 1}`, "", "accordion-set");
59+
}
60+
}
6661

6762
// Use dynamic content for the rest
68-
for (let i = 1; i < accordionCount; i++) {
63+
for (let i = 0; i < accordionCount; i++) {
6964
config.slots.push({
7065
content:
7166
tools.component("pfe-accordion-header", {}, [
7267
{
7368
content: tools.customTag({
7469
tag: "h2",
75-
content: tools.autoHeading()
70+
content: customContent ? headings[i] : tools.autoHeading()
7671
})
7772
}
7873
]) +
7974
tools.component("pfe-accordion-panel", {}, [
8075
{
81-
content: tools.autoContent(5, 3)
76+
content: customContent ? panels[i] : tools.autoContent(5, 3)
8277
}
8378
])
8479
});

elements/pfe-accordion/src/pfe-accordion.js

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -64,19 +64,27 @@ class PfeAccordion extends PFElement {
6464
return "pfe-accordion.html";
6565
}
6666

67+
get schemaUrl() {
68+
return "pfe-accordion.json";
69+
}
70+
6771
static get observedAttributes() {
68-
return ["theme", "color", "on"];
72+
return ["on"];
6973
}
7074

7175
static get cascadingAttributes() {
7276
return {
73-
color: "pfe-accordion-header, pfe-accordion-panel",
7477
on: "pfe-accordion-header, pfe-accordion-panel"
7578
};
7679
}
7780

81+
// Declare the type of this component
82+
static get PfeType() {
83+
return PFElement.PfeTypes.Container;
84+
}
85+
7886
constructor() {
79-
super(PfeAccordion);
87+
super(PfeAccordion, { type: PfeAccordion.PfeType });
8088
}
8189

8290
connectedCallback() {
@@ -101,21 +109,6 @@ class PfeAccordion extends PFElement {
101109

102110
attributeChangedCallback(attr, oldVal, newVal) {
103111
super.attributeChangedCallback(attr, oldVal, newVal);
104-
105-
if (attr === "color") {
106-
const headers = this.querySelectorAll(PfeAccordionHeader.tag);
107-
108-
if (newVal === "striped") {
109-
[...headers].forEach((header, index) => {
110-
const headerClass = index % 2 ? "even" : "odd";
111-
header.classList.add(headerClass);
112-
});
113-
} else {
114-
[...headers].forEach((header, index) => {
115-
header.classList.remove("even", "odd");
116-
});
117-
}
118-
}
119112
}
120113

121114
toggle(index) {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-04/schema#",
3+
"title": "Accordion",
4+
"description": "This element renders content in an accordion.",
5+
"type": "object",
6+
"tag": "pfe-accordion",
7+
"class": "pfe-accordion",
8+
"category": "container",
9+
"properties": {
10+
"slots": {
11+
"title": "Slots",
12+
"description": "Definition of the supported slots",
13+
"type": "object",
14+
"properties": {
15+
"default": {
16+
"title": "Default",
17+
"type": "array",
18+
"namedSlot": false,
19+
"items": {
20+
"oneOf": [{
21+
"$ref": "pfe-accordion-header"
22+
}, {
23+
"$ref": "pfe-accordion-panel"
24+
}]
25+
}
26+
}
27+
}
28+
},
29+
"attributes": {
30+
"title": "Attributes",
31+
"type": "object",
32+
"properties": {
33+
"on": {
34+
"title": "Context",
35+
"type": "string",
36+
"enum": [
37+
"light",
38+
"dark"
39+
],
40+
"default": "light",
41+
"prefixed": false
42+
}
43+
},
44+
"required": ["on"]
45+
}
46+
},
47+
"required": ["slots", "attributes"],
48+
"additionalProperties": false
49+
}

elements/pfe-accordion/test/pfe-accordion_test.html

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -237,25 +237,6 @@ <h2>Where do the main characters work as adults?</h2>
237237

238238
sinon.assert.calledWith(spy, 'pfe-accordion-header: The first child in the light DOM must be a Header level tag (h1, h2, h3, h4, h5, or h6)');
239239
});
240-
241-
test('it should add an even or odd class to the headers if the theme is striped', () => {
242-
const pfeAccordion = document.querySelector('pfe-accordion');
243-
const headers = [...pfeAccordion.querySelectorAll('pfe-accordion-header')];
244-
245-
pfeAccordion.setAttribute('color', 'striped');
246-
247-
/*
248-
* this can be confusing. index of 0 is really "even" but visually,
249-
* the first heading (index of 0) is 1 making it have a class of odd.
250-
*/
251-
headers.forEach((header, index) => {
252-
if (index % 2 === 0) {
253-
assert.isTrue(header.classList.contains('odd'));
254-
} else {
255-
assert.isTrue(header.classList.contains('even'));
256-
}
257-
});
258-
});
259240
});
260241
</script>
261242
</body>

0 commit comments

Comments
 (0)