Skip to content
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
18 changes: 13 additions & 5 deletions src/verifier-credential.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,15 +179,23 @@ const styles = `
color: var(--vp-ink-2, #47536a); overflow: auto; max-height: 460px; white-space: pre; }
.check { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 4px 16px;
padding: 7px 0; font-size: .9rem; border-bottom: 1px solid var(--vp-rule, #d9dee7); }
@container (max-width: 380px) { .check { grid-template-columns: 1fr; } }
.check:last-child { border-bottom: 0; }
.check .k { color: var(--vp-ink-2, #47536a); }
/* The mark sits inline, at the start of the value's first line. As a flex
item beside the text, it stayed at the left edge once the text wrapped,
a column away from the right-aligned words it belongs to. A word too long
for the row, such as an issuer's did:key, breaks rather than running off
the card; the mark then sits just above it. */
.check .v { text-align: right; font-weight: 500; overflow-wrap: anywhere; }
a column away from the words it belongs to. A word too long for the row,
such as an issuer's did:key, breaks rather than running off the card; the
mark then sits just above it. Start and end, not left and right, so a
right-to-left page mirrors the layout rather than breaking it. */
.check .v { text-align: end; font-weight: 500; overflow-wrap: anywhere; }
/* A narrow card, as on a phone, stacks each row: the label, then its value
on the line under it, starting where the label does. Aligned to the
end, a short value sat at the far edge, away from its label, and a long one
wrapped ragged. After .check .v, so its text-align wins. */
@container (max-width: 380px) {
.check { grid-template-columns: 1fr; }
.check .v { text-align: start; }
}
.mark { width: 15px; height: 15px; border-radius: 50%; display: inline-grid; place-items: center;
font-size: 10px; font-weight: 700; color: #fff; vertical-align: 2px; margin-right: 2px; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
Expand Down
64 changes: 61 additions & 3 deletions test/browser/states.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,17 +268,75 @@ test("a value too long for the row, such as an issuer's did:key, stays inside th
const first = range.getClientRects()[0]!;
return {
fits: card.scrollWidth <= card.clientWidth,
// Too long to sit beside it, so on the line just above, at the right
// Too long to sit beside it, so on the line just above, at the left
// where the identifier starts (the rows stack at this width)
above: first.top - mark.bottom,
fromRight: Math.abs(mark.right - first.right),
fromLeft: Math.abs(mark.left - first.left),
};
});
expect(layout.fits, 'the identifier runs off the card').toBe(true);
expect(layout.above, 'the mark is not just above the identifier').toBeGreaterThanOrEqual(-2);
expect(layout.above, 'the mark is not just above the identifier').toBeLessThan(10);
expect(layout.fromRight, 'the mark is not at the right, over the identifier').toBeLessThan(10);
expect(layout.fromLeft, 'the mark is not at the left, over the identifier').toBeLessThan(10);
});

/**
* Where each value starts, against its label and its row. In a narrow card
* (380px or less) each row stacks, label over value; a wide card keeps them
* side by side.
*/
const valueEdges = (page: Page) =>
page.evaluate(() =>
[...document.getElementById('vc')!.shadowRoot!.querySelectorAll('.check')].map((row) => {
const label = row.querySelector('.k')!.getBoundingClientRect();
const mark = row.querySelector('.mark')!.getBoundingClientRect();
const range = document.createRange();
range.selectNodeContents(row.querySelector('.mark + span')!.lastChild!);
const lines = [...range.getClientRects()];
return {
label: row.querySelector('.k')!.textContent,
below: mark.top >= label.bottom - 1,
fromLabelLeft: Math.abs(mark.left - label.left),
fromLabelRight: Math.abs(mark.right - label.right),
fromRowRight: Math.abs(row.getBoundingClientRect().right - lines[lines.length - 1]!.right),
};
}),
);

test('stacked in a right-to-left page, each value starts under its label, at the right', async ({ page }) => {
// The alignment is start and end, not left and right, so it follows the
// page's direction. The mark's 2px margin is on its right whichever way.
await page.setViewportSize({ width: 360, height: 900 });
await page.evaluate(() => document.documentElement.setAttribute('dir', 'rtl'));
await pick(page, 'Verified');
for (const r of await valueEdges(page)) {
expect(r.below, `${r.label}: the value is not under its label`).toBe(true);
expect(r.fromLabelRight, `${r.label}: the value does not start under its label`).toBeLessThan(3);
}
});

for (const situation of ['Verified', 'Registry offline']) {
test(`stacked on a phone, each value starts under its label (${situation})`, async ({ page }) => {
// Right-aligned under a label at the left, a short value sat at the far
// edge of the card and a long one wrapped ragged, away from its label
await page.setViewportSize({ width: 360, height: 900 });
await pick(page, situation);
for (const r of await valueEdges(page)) {
expect(r.below, `${r.label}: the value is not under its label`).toBe(true);
expect(r.fromLabelLeft, `${r.label}: the value does not start under its label`).toBeLessThan(1);
}
});

test(`side by side in a wide card, each value keeps to the right (${situation})`, async ({ page }) => {
await page.setViewportSize({ width: 1024, height: 900 });
await pick(page, situation);
for (const r of await valueEdges(page)) {
expect(r.below, `${r.label}: the value went under its label`).toBe(false);
expect(r.fromRowRight, `${r.label}: the value does not end at the right`).toBeLessThan(1);
}
});
}

/**
* Found in review. The spoken headline was HTML-escaped on its way into a
* `textContent` assignment, so a screen reader on the unconfirmed-issuer card
Expand Down
Loading