Support aria markup in SVG charts
Add "aria-labelled-by", "title" and "desc" to describe the svg charts in screen readers.
This commit is contained in:
parent
9c5d44caf0
commit
28dfbe7e55
|
@ -38,6 +38,20 @@ abstract class Chart implements Drawable
|
|||
*/
|
||||
protected $palette;
|
||||
|
||||
/**
|
||||
* The title of this chart, used for providing accessibility features
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $title;
|
||||
|
||||
/**
|
||||
* The description for this chart, mandatory for providing accessibility features
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $description;
|
||||
|
||||
/**
|
||||
* Create a new chart object and create internal objects
|
||||
*
|
||||
|
@ -86,7 +100,6 @@ abstract class Chart implements Drawable
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Render this graph and return the created SVG
|
||||
*
|
||||
* @return string The SVG created by the SvgRenderer
|
||||
|
@ -105,6 +118,11 @@ abstract class Chart implements Drawable
|
|||
$this->renderer->setXAspectRatioAlignment(SVGRenderer::X_ASPECT_RATIO_MIN);
|
||||
$this->renderer->setYAspectRatioAlignment(SVGRenderer::Y_ASPECT_RATIO_MIN);
|
||||
}
|
||||
|
||||
$this->renderer->setAriaDescription($this->description);
|
||||
$this->renderer->setAriaTitle($this->title);
|
||||
$this->renderer->getCanvas()->setAriaRole('presentation');
|
||||
|
||||
$this->renderer->getCanvas()->addElement($this);
|
||||
return $this->renderer->render();
|
||||
}
|
||||
|
|
|
@ -84,6 +84,13 @@ class GridChart extends Chart
|
|||
*/
|
||||
private $tooltips = array();
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->title = t('Grid Chart');
|
||||
$this->description = t('Contains data in a bar or line chart.');
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current dataset has the proper structure for this chart.
|
||||
*
|
||||
|
|
|
@ -50,6 +50,13 @@ class PieChart extends Chart
|
|||
*/
|
||||
private $noCaption = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->title = t('Pie Chart');
|
||||
$this->description = t('Contains data in a pie chart.');
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the given pies have the correct format
|
||||
*
|
||||
|
|
|
@ -43,6 +43,13 @@ class Canvas implements Drawable
|
|||
*/
|
||||
private $rect;
|
||||
|
||||
/**
|
||||
* The aria role used to describe this canvas' purpose in the accessibility tree
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $ariaRole;
|
||||
|
||||
/**
|
||||
* Create this canvas
|
||||
*
|
||||
|
@ -111,6 +118,23 @@ class Canvas implements Drawable
|
|||
$innerContainer->appendChild($child->toSvg($ctx));
|
||||
}
|
||||
|
||||
if (isset($this->ariaRole)) {
|
||||
$outer->setAttribute('role', $this->ariaRole);
|
||||
}
|
||||
return $outer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the aria role used to determine the meaning of this canvas in the accessibility tree
|
||||
*
|
||||
* The role 'presentation' will indicate that the purpose of this canvas is entirely decorative, while the role
|
||||
* 'img' will indicate that the canvas contains an image, with a possible title or a description. For other
|
||||
* possible roles, see http://www.w3.org/TR/wai-aria/roles
|
||||
*
|
||||
* @param $role string The aria role to set
|
||||
*/
|
||||
public function setAriaRole($role)
|
||||
{
|
||||
$this->ariaRole = $role;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,27 @@ class SVGRenderer
|
|||
*/
|
||||
private $svg;
|
||||
|
||||
/**
|
||||
* The description of this SVG, useful for screen readers
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $ariaDescription;
|
||||
|
||||
/**
|
||||
* The title of this SVG, useful for screen readers
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $ariaTitle;
|
||||
|
||||
/**
|
||||
* The aria role used by this svg element
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $ariaRole = 'img';
|
||||
|
||||
/**
|
||||
* The root layer for all elements
|
||||
*
|
||||
|
@ -126,6 +147,7 @@ class SVGRenderer
|
|||
$svg = $this->document->createElement('svg');
|
||||
$svg->setAttribute('xmlns', 'http://www.w3.org/2000/svg');
|
||||
$svg->setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
|
||||
$svg->setAttribute('role', $this->ariaRole);
|
||||
$svg->setAttribute('width', '100%');
|
||||
$svg->setAttribute('height', '100%');
|
||||
$svg->setAttribute(
|
||||
|
@ -150,6 +172,42 @@ class SVGRenderer
|
|||
return $svg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add aria title and description
|
||||
*
|
||||
* Adds an aria title and desc element to the given SVG node, which are used to describe this SVG by accessibility
|
||||
* tools such as screen readers.
|
||||
*
|
||||
* @param DOMNode $svg The SVG DOMNode to which the aria attributes should be attached
|
||||
* @param $title The title text
|
||||
* @param $description The description text
|
||||
*/
|
||||
private function addAriaDescription (DOMNode $svg, $titleText, $descriptionText)
|
||||
{
|
||||
$doc = $svg->ownerDocument;
|
||||
|
||||
$titleId = $descId = '';
|
||||
if (isset ($this->ariaTitle)) {
|
||||
$titleId = 'aria-title-' . $this->stripNonAlphanumeric($titleText);
|
||||
$title = $doc->createElement('title');
|
||||
$title->setAttribute('id', $titleId);
|
||||
|
||||
$title->appendChild($doc->createTextNode($titleText));
|
||||
$svg->appendChild($title);
|
||||
}
|
||||
|
||||
if (isset ($this->ariaDescription)) {
|
||||
$descId = 'aria-desc-' . $this->stripNonAlphanumeric($descriptionText);
|
||||
$desc = $doc->createElement('desc');
|
||||
$desc->setAttribute('id', $descId);
|
||||
|
||||
$desc->appendChild($doc->createTextNode($descriptionText));
|
||||
$svg->appendChild($desc);
|
||||
}
|
||||
|
||||
$svg->setAttribute('aria-labelledby', join(' ', array($titleId, $descId)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises the XML-document, SVG-element and this figure's root canvas
|
||||
*
|
||||
|
@ -172,6 +230,7 @@ class SVGRenderer
|
|||
{
|
||||
$this->createRootDocument();
|
||||
$ctx = $this->createRenderContext();
|
||||
$this->addAriaDescription($this->svg, $this->ariaTitle, $this->ariaDescription);
|
||||
$this->svg->appendChild($this->rootCanvas->toSvg($ctx));
|
||||
$this->document->formatOutput = true;
|
||||
return $this->document->saveXML();
|
||||
|
@ -232,4 +291,40 @@ class SVGRenderer
|
|||
{
|
||||
$this->yAspectRatio = $alignment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the aria description, that is used as a title for this SVG in screen readers
|
||||
*
|
||||
* @param $text
|
||||
*/
|
||||
public function setAriaTitle($text)
|
||||
{
|
||||
$this->ariaTitle = $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the aria description, that is used to describe this SVG in screen readers
|
||||
*
|
||||
* @param $text
|
||||
*/
|
||||
public function setAriaDescription($text)
|
||||
{
|
||||
$this->ariaDescription = $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the aria role, that is used to describe the purpose of this SVG in screen readers
|
||||
*
|
||||
* @param $text
|
||||
*/
|
||||
public function setAriaRole($text)
|
||||
{
|
||||
$this->ariaRole = $text;
|
||||
}
|
||||
|
||||
|
||||
private function stripNonAlphanumeric($str)
|
||||
{
|
||||
return preg_replace('/[^A-Za-z]+/', '', $str);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -341,6 +341,8 @@ class Monitoring_AlertsummaryController extends Controller
|
|||
public function createHealingChart()
|
||||
{
|
||||
$gridChart = new GridChart();
|
||||
$gridChart->title = t('Healing Chart');
|
||||
$gridChart->description = t('Notifications and average reaction time per hour.');
|
||||
|
||||
$gridChart->alignTopLeft();
|
||||
$gridChart->setAxisLabel($this->createPeriodDescription(), mt('monitoring', 'Notifications'))
|
||||
|
@ -477,6 +479,8 @@ class Monitoring_AlertsummaryController extends Controller
|
|||
public function createDefectImage()
|
||||
{
|
||||
$gridChart = new GridChart();
|
||||
$gridChart->title = t('Defect Chart');
|
||||
$gridChart->description = t('Notifications and defects per hour');
|
||||
|
||||
$gridChart->alignTopLeft();
|
||||
$gridChart->setAxisLabel($this->createPeriodDescription(), mt('monitoring', 'Notifications'))
|
||||
|
|
|
@ -236,6 +236,9 @@ class Monitoring_ChartController extends Controller
|
|||
$unknownBars[] = array($servicegroup->servicegroup, $servicegroup->services_unknown_unhandled);
|
||||
}
|
||||
$this->view->chart = new GridChart();
|
||||
$this->view->chart->title = t('Service Group Chart');
|
||||
$this->view->chart->description = t('Contains service states for each service group.');
|
||||
|
||||
$this->view->chart->alignTopLeft();
|
||||
$this->view->chart->setAxisLabel('', mt('monitoring', 'Services'))
|
||||
->setXAxis(new StaticAxis())
|
||||
|
@ -292,6 +295,9 @@ class Monitoring_ChartController extends Controller
|
|||
}
|
||||
$tooltip = mt('monitoring', '<b>{title}:</b><br> {value} of {sum} hosts are {label}');
|
||||
$this->view->chart = new GridChart();
|
||||
$this->view->chart->title = t('Host Group Chart');
|
||||
$this->view->chart->description = t('Contains host states of each service group.');
|
||||
|
||||
$this->view->chart->alignTopLeft();
|
||||
$this->view->chart->setAxisLabel('', mt('monitoring', 'Hosts'))
|
||||
->setXAxis(new StaticAxis())
|
||||
|
|
Loading…
Reference in New Issue