Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infrastructure: Add support for opening examples in CodePen #1110

Merged
merged 15 commits into from
Sep 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions examples/coding-template/Example-Template.html
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ <h1>EXAMPLE_NAME Example</h1>
</ul>

<section>
<h2 id="ex_label">Example</h2>
<div class="example-header">
<h2 id="ex_label">Example</h2>
</div>
<div role="separator" id="ex_start_sep" aria-labelledby="ex_start_sep ex_label" aria-label="Start of"></div>
<!--
Note the ID of the following div that contains the example HTML is used as a parameter for the sourceCode.add() function.
Expand Down Expand Up @@ -189,7 +191,7 @@ <h2 id="rps_label">Role, Property, State, and Tabindex Attributes</h2>
<section>
<h2>Javascript and CSS Source Code</h2>
<!-- After the js and css files are named with the name of this example, change the href and text of the following 2 links to refer to the appropriate js and css files. -->
<ul>
<ul id="cssJsFiles">
<li>
CSS:
<a href="css/example_name.css" type="tex/css">example_name.css</a>
Expand All @@ -212,7 +214,7 @@ <h2 id="sc1_label">HTML Source Code</h2>
If you change the ID of either the 'ex1' div or the 'sc1' pre, be sure to update the sourceCode.add function parameters.
-->
<script>
sourceCode.add('sc1', 'ex1');
sourceCode.add('sc1', 'ex1', 'ex_label', 'cssJsFiles');
sourceCode.make();
</script>
</section>
Expand Down
69 changes: 69 additions & 0 deletions examples/css/core.css
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,72 @@ table.data.attributes tbody th,
table.data.attributes tbody td {
border: 1px solid silver;
}

/* CodePen button */
.example-header {
display: flex;
align-items: center;
margin-top: 3rem;
page-break-after: avoid;
page-break-inside: avoid;
font: 100% sans-serif;
font-family: inherit;
line-height: 1.2;
hyphens: manual;
}

.example-header > :first-child {
margin: 0;
}

.example-header > :first-child + * {
margin-left: 1em;
}

.example-header button {
display: inline-block;
position: relative;
padding: 0.4em 0.7em;
border: 1px solid hsl(213, 71%, 49%);
border-radius: 5px;
box-shadow: 0 1px 2px hsl(216, 27%, 55%);
color: #fff;
font-size: inherit;
text-shadow: 0 -1px 1px hsl(216, 27%, 25%);
background-color: hsl(216, 82%, 51%);
background-image: linear-gradient(to bottom, hsl(216, 82%, 53%), hsl(216, 82%, 47%));
}

.example-header button:hover {
border-color: hsl(213, 71%, 29%);
background-color: hsl(216, 82%, 31%);
background-image: linear-gradient(to bottom, hsl(216, 82%, 33%), hsl(216, 82%, 27%));
cursor: default;
}

.example-header button:focus {
outline: none;
ZoeBijl marked this conversation as resolved.
Show resolved Hide resolved
}

.example-header button:focus::before {
position: absolute;
z-index: -1;

/* button border width - outline width - offset */
top: calc(-1px - 3px - 3px);
right: calc(-1px - 3px - 3px);
bottom: calc(-1px - 3px - 3px);
left: calc(-1px - 3px - 3px);
border: 3px solid hsl(213, 71%, 49%);

/* button border radius + outline width + offset */
border-radius: calc(5px + 3px + 3px);
content: '';
}

.example-header button:active {
border-color: hsl(213, 71%, 49%);
background-color: hsl(216, 82%, 31%);
background-image: linear-gradient(to bottom, hsl(216, 82%, 53%), hsl(216, 82%, 47%));
box-shadow: inset 0 3px 5px 1px hsl(216, 82%, 30%);
}
108 changes: 107 additions & 1 deletion examples/js/examples.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,26 @@ var VOID_ELEMENTS = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
aria.widget.SourceCode = function () {
this.location = new Array();
this.code = new Array();
this.exampleHeader = new Array();
this.resources = new Array();
};

/**
* Adds source code
*
* @param {string} locationId - ID of `code` element that will display the example html
* @param {string} codeID - ID of element containing only and all of the html used to render the example widget
* @param {string} exampleHeaderId - ID of header element under which the "Open in Codepen" button belongs
* @param {string} cssJsFilesId - ID of element containing links to all the relevent js and css files used for the example widget
*
* @method add
* @memberof aria.widget.SourceCode
*/
aria.widget.SourceCode.prototype.add = function (locationId, codeId) {
aria.widget.SourceCode.prototype.add = function (locationId, codeId, exampleHeaderId, cssJsFilesId) {
this.location[this.location.length] = locationId;
this.code[this.code.length] = codeId;
this.exampleHeader[this.exampleHeader.length] = exampleHeaderId;
this.resources[this.resources.length] = cssJsFilesId;
};

/**
Expand All @@ -66,6 +75,11 @@ aria.widget.SourceCode.prototype.make = function () {
if (sourceCodeNode.innerHTML.startsWith('<br>')) {
sourceCodeNode.innerHTML = sourceCodeNode.innerHTML.replace('<br>', '');
}

// Adds the "Open In CodePen" button by the example header
if (this.exampleHeader[i]) {
addOpenInCodePenForm(i, this.exampleHeader[i], this.code[i], this.resources[i]);
}
}
};

Expand Down Expand Up @@ -300,4 +314,96 @@ function indentLines (input, indentation) {
return lines.join('\n');
}

/**
* Creates and adds an "Open in CodePen" button
*
* @param {String} exampleIndex - the example number, if there are multiple examples
* @param {String} exampleHeaderId - the example header to place the button next to
* @param {String} exampleCodeId - the example html code
* @param {String} exampleFilesId - the element containing all relevent CSS and JS file
*/
function addOpenInCodePenForm (exampleIndex, exampleHeaderId, exampleCodeId, exampleFilesId) {
var jsonInputId = 'codepen-data-ex-' + exampleIndex;
var buttonId = exampleCodeId + '-codepenbutton'

var form = document.createElement('form');
form.setAttribute('action', 'https://codepen.io/pen/define');
form.setAttribute('method', 'POST');
form.setAttribute('target', '_blank');

var input = document.createElement('input');
input.setAttribute('id', jsonInputId);
input.setAttribute('type', 'hidden');
input.setAttribute('name', 'data');

var button = document.createElement('button');
button.innerText = 'Open In CodePen';

form.appendChild(input);
form.appendChild(button);

var exampleHeader = document.getElementById(exampleHeaderId);
exampleHeader.parentNode.insertBefore(form, exampleHeader.nextSibling);

// Correct the indentation for the example html
var indentedExampleHtml = document.getElementById(exampleCodeId).innerHTML;
indentedExampleHtml = indentedExampleHtml.replace(/^\n+/, '');
var indentation = indentedExampleHtml.match(/^\s+/)[0];
var exampleHtml = indentedExampleHtml.replace(new RegExp('^' + indentation, 'gm'), '');

var postJson = {
html: exampleHtml,
css: '',
js: '',
head: '<base href="' + location.href + '">'
};

var totalFetchedFiles = 0;
var fileLinks = document.querySelectorAll('#' + exampleFilesId + ' a');

for (let fileLink of fileLinks) {

var request = new XMLHttpRequest();

request.open('GET', fileLink.href, true);
request.onload = function() {
var href = this.responseURL;
if (this.status >= 200 && this.status < 400) {
if (href.indexOf('css') !== -1) {
postJson.css = postJson.css.concat(this.response);
}
if (href.indexOf('js') !== -1) {
postJson.js = postJson.js.concat(this.response);
}
totalFetchedFiles++;
}
else {
hideButton(buttonId, "Could not load resource: " + href);
}
};
request.onerror = function() {
hideButton(buttonId, "Could not load resource: " + fileLink.href);
};
request.send();
}

var timerId = setInterval(() => {
console.log(totalFetchedFiles);
if (totalFetchedFiles === fileLinks.length) {
document.getElementById(jsonInputId).value = JSON.stringify(postJson);
clearInterval(timerId);
}
}, 500);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a thing that needs to be fixed, but it looks like async/await is supported in all modern browsers. If we wanted to avoid using manual intervals, pushing all the request.send promises into an array then doing await Promise.all could work.


setTimeout(() => {
clearInterval(timerId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to have a hideButton call here too, with an error message about loading files timing out?

}, 10000);
}

function hideButton(buttonId, errorMsg) {
let button = document.querySelector(buttonId);
button.style.display = "none";
console.log("Removing 'Open in Codepen button'. " + errorMsg);
}

var sourceCode = new aria.widget.SourceCode();