Add landmarks (#564)

Use HTML semantic elements to set the landmarks of the page.

This is helpful for assistive technologies to determine the different regions of content. In our case it's useful for jumping between the different islands that we use to group the form controls.
This commit is contained in:
Guillermo Peralta Scura 2020-01-26 17:14:31 -03:00 committed by GitHub
parent fc350f2ecd
commit 67eca2bda1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 757 additions and 697 deletions

View File

@ -81,9 +81,11 @@
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<header>
<h1 class="visually-hidden">Excalidraw</h1>
</header>
<div id="root"></div>
<aside>
<!-- https://github.com/tholman/github-corners -->
<svg
xmlns="http://www.w3.org/2000/svg"
@ -111,5 +113,6 @@
/>
</a>
</svg>
</aside>
</body>
</html>

View File

@ -38,7 +38,8 @@
"cartoonist": "Cartoonist",
"fileTitle": "File title",
"colorPicker": "Color picker",
"canvasBackground": "Canvas background"
"canvasBackground": "Canvas background",
"drawingCanvas": "Drawing Canvas"
},
"buttons": {
"clearReset": "Clear the canvas & reset background color",
@ -48,7 +49,8 @@
"save": "Save",
"load": "Load",
"getShareableLink": "Get shareable link",
"close": "Close"
"close": "Close",
"selectLanguage": "Select Language"
},
"alerts": {
"clearReset": "This will clear the whole canvas. Are you sure?",
@ -67,5 +69,10 @@
"line": "Line",
"text": "Text",
"lock": "Keep selected tool active after drawing"
},
"headings": {
"canvasActions": "Canvas actions",
"selectedShapeActions": "Selected shape actions",
"shapes": "Shapes"
}
}

View File

@ -38,7 +38,8 @@
"cartoonist": "Caricatura",
"fileTitle": "Título del archivo",
"colorPicker": "Selector de color",
"canvasBackground": "Fondo del lienzo"
"canvasBackground": "Fondo del lienzo",
"drawingCanvas": "Lienzo de dibujo"
},
"buttons": {
"clearReset": "Limpiar lienzo y reiniciar el color de fondo",
@ -49,7 +50,8 @@
"load": "Cargar",
"getShareableLink": "Obtener enlace para compartir",
"showExportDialog": "Mostrar diálogo para exportar",
"close": "Cerrar"
"close": "Cerrar",
"selectLanguage": "Select Language"
},
"alerts": {
"clearReset": "Esto limpiará todo el lienzo. Estás seguro?",
@ -66,6 +68,12 @@
"ellipse": "Elipse",
"arrow": "Flecha",
"line": "Línea",
"text": "Texto"
"text": "Texto",
"lock": "Mantener la herramienta seleccionada activa después de dibujar"
},
"headings": {
"canvasActions": "Acciones del lienzo",
"selectedShapeActions": "Acciones de la forma seleccionada",
"shapes": "Formas"
}
}

View File

@ -1,4 +1,5 @@
import React from "react";
import { useTranslation } from "react-i18next";
export function LanguageList<T>({
onClick,
@ -9,12 +10,15 @@ export function LanguageList<T>({
onClick: (value: string) => void;
currentLanguage: string;
}) {
const { t } = useTranslation();
return (
<React.Fragment>
<select
className="language-select"
onChange={({ target }) => onClick(target.value)}
value={currentLanguage}
aria-label={t("buttons.selectLanguage")}
>
{languages.map(language => (
<option key={language.lng} value={language.lng}>

View File

@ -51,6 +51,7 @@ export function LockIcon(props: LockIconProps) {
id={props.id}
onChange={props.onChange}
checked={props.checked}
aria-label={props.title}
/>
<div className="ToolIcon__icon">
{props.checked ? ICONS.CHECKED : ICONS.UNCHECKED}

View File

@ -606,18 +606,32 @@ export class App extends React.Component<any, AppState> {
<FixedSideContainer side="top">
<div className="App-menu App-menu_top">
<Stack.Col gap={4} align="end">
<div className="App-right-menu">
<h2 className="visually-hidden">Canvas actions</h2>
<section
className="App-right-menu"
aria-labelledby="canvas-actions-title"
>
<h2 className="visually-hidden" id="canvas-actions-title">
{t("headings.canvasActions")}
</h2>
<Island padding={4}>{this.renderCanvasActions()}</Island>
</div>
<div className="App-right-menu">
</section>
<section
className="App-right-menu"
aria-labelledby="selected-shape-title"
>
<h2 className="visually-hidden" id="selected-shape-title">
{t("headings.selectedShapeActions")}
</h2>
{this.renderSelectedShapeActions(elements)}
</div>
</section>
</Stack.Col>
<section aria-labelledby="shapes-title">
<Stack.Col gap={4} align="start">
<Stack.Row gap={1}>
<Island padding={1}>
<h2 className="visually-hidden">Shapes</h2>
<h2 className="visually-hidden" id="shapes-title">
{t("headings.shapes")}
</h2>
<Stack.Row gap={1}>{this.renderShapesSwitcher()}</Stack.Row>
</Island>
<LockIcon
@ -634,9 +648,11 @@ export class App extends React.Component<any, AppState> {
/>
</Stack.Row>
</Stack.Col>
</section>
<div />
</div>
</FixedSideContainer>
<main>
<canvas
id="canvas"
style={{
@ -809,7 +825,11 @@ export class App extends React.Component<any, AppState> {
);
if (isTextElement(element)) {
element = newTextElement(element, "", this.state.currentItemFont);
element = newTextElement(
element,
"",
this.state.currentItemFont,
);
}
type ResizeTestType = ReturnType<typeof resizeTest>;
@ -1051,7 +1071,10 @@ export class App extends React.Component<any, AppState> {
}
if (resizeHandle) {
resizeHandle = normalizeResizeHandle(element, resizeHandle);
resizeHandle = normalizeResizeHandle(
element,
resizeHandle,
);
}
normalizeDimensions(element);
@ -1174,8 +1197,13 @@ export class App extends React.Component<any, AppState> {
this.setState({});
}
if (resizingElement && isInvisiblySmallElement(resizingElement)) {
elements = elements.filter(el => el.id !== resizingElement.id);
if (
resizingElement &&
isInvisiblySmallElement(resizingElement)
) {
elements = elements.filter(
el => el.id !== resizingElement.id,
);
}
// If click occurred on already selected element
@ -1333,11 +1361,15 @@ export class App extends React.Component<any, AppState> {
}}
onMouseMove={e => {
const hasDeselectedButton = Boolean(e.buttons);
if (hasDeselectedButton || this.state.elementType !== "selection") {
if (
hasDeselectedButton ||
this.state.elementType !== "selection"
) {
return;
}
const { x, y } = viewportCoordsToSceneCoords(e, this.state);
const selectedElements = elements.filter(e => e.isSelected).length;
const selectedElements = elements.filter(e => e.isSelected)
.length;
if (selectedElements === 1) {
const resizeElement = getElementWithResizeHandler(
elements,
@ -1354,7 +1386,11 @@ export class App extends React.Component<any, AppState> {
const hitElement = getElementAtPosition(elements, x, y);
document.documentElement.style.cursor = hitElement ? "move" : "";
}}
/>
>
{t("labels.drawingCanvas")}
</canvas>
</main>
<footer role="contentinfo">
<LanguageList
onClick={lng => {
i18n.changeLanguage(lng);
@ -1362,6 +1398,7 @@ export class App extends React.Component<any, AppState> {
languages={languages}
currentLanguage={parseDetectedLang(i18n.language)}
/>
</footer>
</div>
);
}