Skip to content

Commit 1a4a537

Browse files
committed
Enhance H5P build page with user prompts for feedback and improved navigation; update styles for better layout and readability
1 parent 294cf20 commit 1a4a537

7 files changed

Lines changed: 136 additions & 22 deletions

File tree

‎docs/h5p-build-page.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
The source is not uploaded: the package is built in the browser. A link can carry the source in its fragment, which no server sees: `/h5p/build/#source=<base64url>`, the bytes being the source's JSON as UTF-8, or that JSON compressed with raw deflate (Node's `zlib.deflateRawSync`, Python's `zlib.compressobj(wbits=-15)`) to keep the link short. A linked source is built at once, unless it names files of its own, which only the person can choose. A page opened by such a link keeps the fragment in step with the source as the person changes it, so the address can be copied to send the changed source on ([below](#the-source-editor)). A source that names a picture by web address gets it only where that host allows CORS.
66

7+
For a person with no source yet, the page gives a prompt to copy into a chat. It carries the address of [the skill page](h5p-skill.md) whole, which a chat's fetch tool needs, and `site.js` gives it a Copy button as it does a code block. Under the steps, and there before anything is built, the page says where a package plays with no H5P site, in a paragraph with two links: [h5p-offline-player](https://github.com/missing-elements/h5p-offline-player), the player it plays in here, is set up on a site of one's own (its setup guide), or is an app that plays with no network. Under that is where to say how it went, in two ways, since a teacher may have no GitHub account. The first is an email, which asks for none, to the address h5p-offline-player's code of conduct and security policy give. The second is a new discussion in h5p-offline-player, where the author is made, which is public. `buildFeedback` in [src/_data/site.js](../src/_data/site.js) writes both addresses, each with the same title and two questions to start from.
8+
79
| File | What it holds |
810
| --- | --- |
911
| [src/h5p/build/index.njk](../src/h5p/build/index.njk) | The page's markup, in the site's layout, and its Content-Security-Policy: GitHub Pages cannot send a Content-Security-Policy header for a site, so a `<meta>` tag is the only policy it gets |

‎e2e/build.spec.ts‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,44 @@ test('a pasted source builds, plays and downloads', async ({ page, baseURL }) =>
118118
expect(problems, problems.join('\n')).toEqual([])
119119
})
120120

121+
test('before anything is built the page gives a prompt to try, and says where a package plays and where to say how it went', async ({ page, context, baseURL }) => {
122+
const { problems, elsewhere } = watch(page, baseURL)
123+
await context.grantPermissions(['clipboard-read', 'clipboard-write'])
124+
await page.goto('/h5p/build/')
125+
// The prompt carries the skill's page at its whole address, which a chat's fetch tool needs, and its button copies it.
126+
const prompt = page.locator('pre.prompt')
127+
await expect(prompt).toHaveText('Read https://missing-elements.github.io/h5p/skill/ and follow it to make an H5P quiz with five questions about the rivers of Europe.')
128+
await page.locator('.docs-header').getByRole('button', { name: 'Copy' }).click()
129+
expect(await page.evaluate(() => navigator.clipboard.readText())).toBe(await prompt.textContent())
130+
131+
// With no H5P site: the player this page plays it in, in two links in a paragraph, to how to set it
132+
// up on a site of one's own and to its app, offline.
133+
const plays = page.getByRole('region', { name: 'Play it without an H5P site' })
134+
await expect(plays.locator('p a')).toHaveCount(2)
135+
await expect(plays.getByRole('link', { name: 'on a site of your own' })).toHaveAttribute('href', 'https://github.com/missing-elements/h5p-offline-player/blob/main/docs/setup.md')
136+
await expect(plays.getByRole('link', { name: 'offline, in its app' })).toHaveAttribute('href', 'https://h5p-offline-player.vercel.app/app/')
137+
138+
// Feedback asks for no account: an email, with a subject and two questions to start from.
139+
const feedback = page.getByRole('region', { name: 'How did it go?' })
140+
const email = feedback.getByRole('link', { name: 'Email feedback' })
141+
await expect(email).toBeVisible()
142+
const letter = new URL((await email.getAttribute('href'))!)
143+
expect(letter.protocol).toBe('mailto:')
144+
expect(letter.pathname).toMatch(/^[^@\s]+@[^@\s]+$/)
145+
expect(letter.searchParams.get('subject')).toBe('Feedback on the H5P build page')
146+
expect(letter.searchParams.get('body')).toBe('What I made (the content type, and the chat that wrote the source):\r\n\r\n\r\nWhat went well, or wrong:\r\n\r\n')
147+
// And for someone with a GitHub account, a new discussion where the author is made, which starts the same.
148+
const discussion = new URL((await feedback.getByRole('link', { name: 'Discuss on GitHub' }).getAttribute('href'))!)
149+
expect(discussion.origin + discussion.pathname).toBe('https://github.com/missing-elements/h5p-offline-player/discussions/new')
150+
expect(discussion.searchParams.get('category')).toBe('general')
151+
expect(discussion.searchParams.get('title')).toBe('Feedback on the H5P build page')
152+
expect(discussion.searchParams.get('body')).toBe(letter.searchParams.get('body')!.replaceAll('\r\n', '\n'))
153+
154+
// They are links: the page still fetches nothing from another site.
155+
expect(elsewhere, 'nothing was fetched from another site').toEqual([])
156+
expect(problems, problems.join('\n')).toEqual([])
157+
})
158+
121159
test('a source with problems gets them all listed, ready for the chat', async ({ page, context, request, baseURL }) => {
122160
const { problems } = watch(page, baseURL)
123161
await context.grantPermissions(['clipboard-read', 'clipboard-write'])

‎e2e/home.spec.ts‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@ import { expect, test } from '@playwright/test'
22

33
/*
44
* The org home, /: a card for each element, one for each tool, the build page, and a footer of
5-
* links; and the build page links the skill's page at the whole address a person gives the chat.
5+
* links; and the build page links the skill's page, which has no card of its own.
66
*/
77

8-
const SKILL = 'https://missing-elements.github.io/h5p/skill/'
9-
108
test('the home page links to the build page, and the build page to the skill’s page, and both answer', async ({ page, request }) => {
119
await page.goto('/')
1210
await expect(page.getByRole('region', { name: 'Web components' }).getByRole('link', { name: 'pdfjs-viewer', exact: true })).toHaveAttribute('href', '/pdfjs-viewer/')
@@ -15,6 +13,6 @@ test('the home page links to the build page, and the build page to the skill’s
1513
expect((await request.get('/h5p/build/')).ok()).toBe(true)
1614

1715
await page.goto('/h5p/build/')
18-
await expect(page.getByRole('link', { name: SKILL, exact: true })).toHaveAttribute('href', SKILL)
16+
await expect(page.getByRole('link', { name: "the h5p-author skill's page" })).toHaveAttribute('href', '/h5p/skill/')
1917
expect((await request.get('/h5p/skill/')).ok()).toBe(true)
2018
})

‎src/_data/site.js‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
// The address the project answers at: the one h5p-offline-player's code of conduct and security
2+
// policy give, and the organization's on GitHub.
3+
const contact = "alekswebnet@gmail.com";
4+
5+
// Where to say how the H5P build page and the h5p-author skill went, in two ways that start from
6+
// the same title and two questions. An email asks for no account, and a teacher may have none on
7+
// GitHub. A new discussion in h5p-offline-player, where the author is made, is public, for someone
8+
// who has one. A mail body's lines end in CRLF (RFC 6068).
9+
const feedbackTitle = "Feedback on the H5P build page";
10+
const feedbackBody = "What I made (the content type, and the chat that wrote the source):\n\n\nWhat went well, or wrong:\n\n";
11+
const buildFeedback = {
12+
email:
13+
`mailto:${contact}?subject=${encodeURIComponent(feedbackTitle)}` +
14+
`&body=${encodeURIComponent(feedbackBody.replaceAll("\n", "\r\n"))}`,
15+
github:
16+
"https://github.com/missing-elements/h5p-offline-player/discussions/new?category=general" +
17+
`&title=${encodeURIComponent(feedbackTitle)}&body=${encodeURIComponent(feedbackBody)}`,
18+
};
19+
120
export default {
221
name: "Missing Elements",
322
url: "https://missing-elements.github.io",
@@ -29,4 +48,5 @@ export default {
2948
repo: "https://github.com/missing-elements/missing-elements.github.io/blob/main/docs/h5p-build-page.md",
3049
},
3150
],
51+
buildFeedback,
3252
};

‎src/assets/site.css‎

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -547,24 +547,21 @@ tbody th a:hover code { text-decoration: underline; }
547547
.home section + section { margin-top: 3rem; }
548548
.home section > h2 { margin: 0 0 0.75rem; padding: 0; border: 0; font-size: 1.5rem; }
549549
.home section > p { max-width: var(--measure); margin: 0 0 1.25rem; color: var(--muted); }
550-
/* The cards two to a row where two fit, each as tall as its row, with its links at its foot. Every
551-
section has the same two columns, so a section of one card keeps it the width of the others. */
552-
.home section { container-type: inline-size; }
553-
.elements {
550+
/* Cards: on the home page, the elements and the tools. Two to a row where two fit and never more,
551+
so one alone keeps the width of the others. Each is as tall as its row, with its links at its
552+
foot. */
553+
.cards {
554554
display: grid;
555-
grid-template-columns: minmax(0, 1fr);
555+
grid-template-columns: repeat(auto-fill, minmax(min(100%, max(18rem, calc((100% - 1rem) / 2))), 1fr));
556556
gap: 1rem;
557557
margin: 0;
558558
padding: 0;
559559
list-style: none;
560560
}
561-
@container (width > 36rem) {
562-
.elements { grid-template-columns: repeat(2, minmax(0, 1fr)); }
563-
}
564-
.elements li { display: flex; flex-direction: column; margin: 0; padding: 1rem 1.25rem; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
565-
.elements h3 { margin: 0 0 0.25rem; font-size: 1.125rem; }
566-
.elements p { margin: 0 0 0.5rem; color: var(--muted); }
567-
.elements .links { display: flex; gap: 1rem; margin-top: auto; font-size: 0.875rem; }
561+
.cards > li { display: flex; flex-direction: column; margin: 0; padding: 1rem 1.25rem; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
562+
.cards :is(h3, h4) { margin: 0 0 0.25rem; font-size: 1.125rem; }
563+
.cards p { margin: 0 0 0.5rem; color: var(--muted); }
564+
.cards .links { display: flex; gap: 1rem; margin-top: auto; font-size: 0.875rem; }
568565
/* The home page's footer: where else the elements are, in columns of links. */
569566
.site-footer { margin-top: 4rem; padding-top: 1.5rem; border-top: 1px solid var(--border); font-size: 0.875rem; }
570567
.footer-links { display: flex; flex-wrap: wrap; gap: 1.5rem 4rem; }
@@ -583,6 +580,11 @@ tbody th a:hover code { text-decoration: underline; }
583580
/* Prose keeps a line a reader can follow; the source, the lists and what plays take the whole width. */
584581
:is(.build, .skill) p:not(.status) { max-width: var(--measure); }
585582

583+
/* A prompt to copy for a chat, in a code block's frame for its Copy button: prose, so it wraps and
584+
is no wider than the prose around it. */
585+
.docs-header :is(.code, pre) { max-width: var(--measure); }
586+
pre.prompt { white-space: pre-wrap; overflow-wrap: anywhere; }
587+
586588
/* H5P build page, /h5p/build/ */
587589
.build {
588590
--ok: var(--live);
@@ -653,7 +655,7 @@ tbody th a:hover code { text-decoration: underline; }
653655
border: 0;
654656
font-size: 1.25rem;
655657
}
656-
.build h3 { margin: 1.5rem 0 0.5rem; font-size: 1rem; }
658+
.build .step h3 { margin: 1.5rem 0 0.5rem; font-size: 1rem; }
657659
.step-marker {
658660
position: absolute;
659661
top: 0;
@@ -796,6 +798,28 @@ tbody th a:hover code { text-decoration: underline; }
796798
.player-box { margin: 1rem 0; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; background: #ffffff; }
797799
.player-box h5p-player { display: block; }
798800

801+
/* Under the steps: where else what was built plays, its title the size of a step's, and a card of
802+
its own for saying how it went, with its two buttons at its end, which share a row where it is
803+
wide enough and take one each where it is not. */
804+
.plays h2 { font-size: 1.25rem; }
805+
.feedback {
806+
display: flex;
807+
flex-wrap: wrap;
808+
align-items: center;
809+
justify-content: space-between;
810+
gap: 0.75rem 1.5rem;
811+
margin-top: 1rem;
812+
padding: 1rem 1.25rem;
813+
border: 1px solid var(--border);
814+
border-radius: var(--radius);
815+
background: var(--surface);
816+
}
817+
.feedback > :first-child { flex: 1 1 28rem; }
818+
.feedback h2 { margin: 0 0 0.25rem; padding: 0; border: 0; font-size: 1.125rem; }
819+
.feedback p { margin: 0; color: var(--muted); }
820+
.feedback-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; }
821+
.feedback-actions .button { flex: 1 1 auto; justify-content: center; }
822+
799823
@media (max-width: 480px) {
800824
.step { --marker: 1.75rem; --indent: calc(var(--marker) + 0.75rem); }
801825
.step-marker { font-size: 0.8125rem; }

‎src/h5p/build/index.njk‎

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,14 @@ csp: "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' da
2727
site takes. Nothing is uploaded: the package is built in your browser, from the libraries this
2828
site serves.
2929
</p>
30-
{#- The skill's page at its whole address, which a person gives the chat as it is. -#}
30+
{#- A prompt to copy, which site.js gives a Copy button as it does a code block. It carries the
31+
skill's page at its whole address, which a chat's fetch tool needs. -#}
3132
<p>
32-
No source yet? Give the chat <a href="{{ site.url }}/h5p/skill/">{{ site.url }}/h5p/skill/</a>
33-
and ask it for an activity: the h5p-author skill there tells it how to write one.
33+
<strong>No source yet?</strong> Paste this into an AI chat, with a subject of your own. It
34+
sends the chat to <a href="/h5p/skill/">the h5p-author skill's page</a>, which tells it how
35+
to write an activity.
3436
</p>
37+
<pre class="language-text prompt" data-label="Prompt">Read {{ site.url }}/h5p/skill/ and follow it to make an H5P quiz with five questions about the rivers of Europe.</pre>
3538
</header>
3639

3740
{#- The steps are drawn as a timeline by site.css: a marker for each, on a rail that runs on to
@@ -104,6 +107,35 @@ csp: "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' da
104107
</p>
105108
</section>
106109

110+
{#- Under the steps, and there before anything is built: where else a package plays, with no H5P
111+
site, which is h5p-offline-player, the player this page plays it in; and where to say how
112+
the build went. -#}
113+
<section class="plays" aria-labelledby="plays-heading">
114+
<h2 id="plays-heading">Play it without an H5P site</h2>
115+
<p>
116+
Any H5P site takes the file you download. With none, use h5p-offline-player, the player this
117+
page plays it in:
118+
<a href="https://github.com/missing-elements/h5p-offline-player/blob/main/docs/setup.md">on a site of your own</a>,
119+
or <a href="https://h5p-offline-player.vercel.app/app/">offline, in its app</a>.
120+
</p>
121+
</section>
122+
123+
{#- Two ways, the email first: it asks for no account, and a teacher may have none on GitHub. -#}
124+
<section class="feedback" aria-labelledby="feedback-heading">
125+
<div>
126+
<h2 id="feedback-heading">How did it go?</h2>
127+
<p>
128+
Say what you made, and what went well or wrong with the chat's source or this page. Write
129+
to the author or, with a GitHub
130+
account, start a public discussion where the h5p-author skill is made.
131+
</p>
132+
</div>
133+
<div class="feedback-actions">
134+
<a class="button button-primary button-large" href="{{ site.buildFeedback.email }}">Email feedback</a>
135+
<a class="button button-large" href="{{ site.buildFeedback.github }}">Discuss on GitHub</a>
136+
</div>
137+
</section>
138+
107139
<footer class="docs-footer">
108140
<p>
109141
Built with <a href="https://github.com/missing-elements/h5p-offline-player">h5p-offline-player</a>

‎src/index.njk‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ description: Standalone, self-hostable web components for viewing documents and
1010

1111
<section aria-labelledby="elements-heading">
1212
<h2 id="elements-heading">Web components</h2>
13-
<ul class="elements" role="list">
13+
<ul class="cards" role="list">
1414
{%- for element in site.elements %}
1515
<li>
1616
<h3><a href="{{ element.docs }}">{{ element.name }}</a></h3>
@@ -27,7 +27,7 @@ description: Standalone, self-hostable web components for viewing documents and
2727
<section aria-labelledby="tools-heading">
2828
<h2 id="tools-heading">Tools</h2>
2929
<p>Apps built on the elements, which run in your browser.</p>
30-
<ul class="elements" role="list">
30+
<ul class="cards" role="list">
3131
{%- for tool in site.tools %}
3232
<li>
3333
<h3><a href="{{ tool.page }}">{{ tool.name }}</a></h3>

0 commit comments

Comments
 (0)