Skip to content

Commit

Permalink
feat(legend): improve tooltip wording (#2439)
Browse files Browse the repository at this point in the history
  • Loading branch information
dej611 authored Jun 3, 2024
1 parent 5c005a3 commit 27c04f4
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 38 deletions.
16 changes: 7 additions & 9 deletions e2e/tests/legend_stories.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,13 @@ test.describe('Legend stories', () => {
'.echLegendItem__label',
);

// check that the first item has a "isolate" title as second line
// check that the first item has a "show" title as third line
const initialLabels = await page.evaluate(() =>
Array.from(document.getElementsByClassName('echLegendItem__label'), (e) => e.getAttribute('title')),
);

expect(initialLabels.map((label) => (label ? label.split('\n')[1] : ''))).toEqual(
new Array(initialLabels.length).fill('Click: isolate series'),
expect(initialLabels.map((label) => (label ? label.split('\n')[2] : ''))).toEqual(
new Array(initialLabels.length).fill('Click to show'),
);

// click on the first item
Expand All @@ -214,8 +214,8 @@ test.describe('Legend stories', () => {
const secondRoundLabels = await page.evaluate(() =>
Array.from(document.getElementsByClassName('echLegendItem__label'), (e) => e.getAttribute('title')),
);
expect(secondRoundLabels.map((label) => (label ? label.split('\n')[1] : ''))).toEqual(
['Click: show all series'].concat(new Array(secondRoundLabels.length - 1).fill('Click: show series')),
expect(secondRoundLabels.map((label) => (label ? label.split('\n')[2] : ''))).toEqual(
['Click to show all'].concat(new Array(secondRoundLabels.length - 1).fill('Click to show')),
);

// now click on the second item (hidden)
Expand All @@ -226,10 +226,8 @@ test.describe('Legend stories', () => {
const thirdRoundLabels = await page.evaluate(() =>
Array.from(document.getElementsByClassName('echLegendItem__label'), (e) => e.getAttribute('title')),
);
expect(thirdRoundLabels.map((label) => (label ? label.split('\n')[1] : ''))).toEqual(
['Click: hide series', 'Click: hide series'].concat(
new Array(secondRoundLabels.length - 2).fill('Click: show series'),
),
expect(thirdRoundLabels.map((label) => (label ? label.split('\n')[2] : ''))).toEqual(
['Click to hide', 'Click to hide'].concat(new Array(secondRoundLabels.length - 2).fill('Click to show')),
);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ exports[`Legend #legendColorPicker should match snapshot after onChange is calle
</ForwardRef>
</div>
<Label label="splita" options={{...}} isToggleable={true} onToggle={[Function (anonymous)]} isSeriesHidden={false} totalSeriesCount={4} hiddenSeriesCount={0}>
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splita\\nClick: isolate series\\nCtrl + click: hide series" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splita; Click: isolate series\\nCtrl + click: hide series">
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splita\\n\\nClick to show\\nCtrl + Click to hide" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splita; Click: to show, Ctrl + Click: to hide">
splita
</div>
</Label>
Expand All @@ -40,7 +40,7 @@ exports[`Legend #legendColorPicker should match snapshot after onChange is calle
</ForwardRef>
</div>
<Label label="splitb" options={{...}} isToggleable={true} onToggle={[Function (anonymous)]} isSeriesHidden={false} totalSeriesCount={4} hiddenSeriesCount={0}>
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splitb\\nClick: isolate series\\nCtrl + click: hide series" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splitb; Click: isolate series\\nCtrl + click: hide series">
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splitb\\n\\nClick to show\\nCtrl + Click to hide" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splitb; Click: to show, Ctrl + Click: to hide">
splitb
</div>
</Label>
Expand All @@ -62,7 +62,7 @@ exports[`Legend #legendColorPicker should match snapshot after onChange is calle
</ForwardRef>
</div>
<Label label="splitc" options={{...}} isToggleable={true} onToggle={[Function (anonymous)]} isSeriesHidden={false} totalSeriesCount={4} hiddenSeriesCount={0}>
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splitc\\nClick: isolate series\\nCtrl + click: hide series" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splitc; Click: isolate series\\nCtrl + click: hide series">
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splitc\\n\\nClick to show\\nCtrl + Click to hide" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splitc; Click: to show, Ctrl + Click: to hide">
splitc
</div>
</Label>
Expand All @@ -84,7 +84,7 @@ exports[`Legend #legendColorPicker should match snapshot after onChange is calle
</ForwardRef>
</div>
<Label label="splitd" options={{...}} isToggleable={true} onToggle={[Function (anonymous)]} isSeriesHidden={false} totalSeriesCount={4} hiddenSeriesCount={0}>
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splitd\\nClick: isolate series\\nCtrl + click: hide series" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splitd; Click: isolate series\\nCtrl + click: hide series">
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splitd\\n\\nClick to show\\nCtrl + Click to hide" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splitd; Click: to show, Ctrl + Click: to hide">
splitd
</div>
</Label>
Expand All @@ -110,7 +110,7 @@ exports[`Legend #legendColorPicker should match snapshot after onClose is called
</ForwardRef>
</div>
<Label label="splita" options={{...}} isToggleable={true} onToggle={[Function (anonymous)]} isSeriesHidden={false} totalSeriesCount={4} hiddenSeriesCount={0}>
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splita\\nClick: isolate series\\nCtrl + click: hide series" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splita; Click: isolate series\\nCtrl + click: hide series">
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splita\\n\\nClick to show\\nCtrl + Click to hide" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splita; Click: to show, Ctrl + Click: to hide">
splita
</div>
</Label>
Expand All @@ -132,7 +132,7 @@ exports[`Legend #legendColorPicker should match snapshot after onClose is called
</ForwardRef>
</div>
<Label label="splitb" options={{...}} isToggleable={true} onToggle={[Function (anonymous)]} isSeriesHidden={false} totalSeriesCount={4} hiddenSeriesCount={0}>
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splitb\\nClick: isolate series\\nCtrl + click: hide series" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splitb; Click: isolate series\\nCtrl + click: hide series">
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splitb\\n\\nClick to show\\nCtrl + Click to hide" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splitb; Click: to show, Ctrl + Click: to hide">
splitb
</div>
</Label>
Expand All @@ -154,7 +154,7 @@ exports[`Legend #legendColorPicker should match snapshot after onClose is called
</ForwardRef>
</div>
<Label label="splitc" options={{...}} isToggleable={true} onToggle={[Function (anonymous)]} isSeriesHidden={false} totalSeriesCount={4} hiddenSeriesCount={0}>
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splitc\\nClick: isolate series\\nCtrl + click: hide series" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splitc; Click: isolate series\\nCtrl + click: hide series">
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splitc\\n\\nClick to show\\nCtrl + Click to hide" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splitc; Click: to show, Ctrl + Click: to hide">
splitc
</div>
</Label>
Expand All @@ -176,7 +176,7 @@ exports[`Legend #legendColorPicker should match snapshot after onClose is called
</ForwardRef>
</div>
<Label label="splitd" options={{...}} isToggleable={true} onToggle={[Function (anonymous)]} isSeriesHidden={false} totalSeriesCount={4} hiddenSeriesCount={0}>
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splitd\\nClick: isolate series\\nCtrl + click: hide series" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splitd; Click: isolate series\\nCtrl + click: hide series">
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splitd\\n\\nClick to show\\nCtrl + Click to hide" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splitd; Click: to show, Ctrl + Click: to hide">
splitd
</div>
</Label>
Expand Down Expand Up @@ -216,7 +216,7 @@ exports[`Legend #legendColorPicker should render colorPicker when color is click
</ForwardRef>
</div>
<Label label="splita" options={{...}} isToggleable={true} onToggle={[Function (anonymous)]} isSeriesHidden={false} totalSeriesCount={4} hiddenSeriesCount={0}>
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splita\\nClick: isolate series\\nCtrl + click: hide series" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splita; Click: isolate series\\nCtrl + click: hide series">
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splita\\n\\nClick to show\\nCtrl + Click to hide" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splita; Click: to show, Ctrl + Click: to hide">
splita
</div>
</Label>
Expand Down Expand Up @@ -251,7 +251,7 @@ exports[`Legend #legendColorPicker should render colorPicker when color is click
</ForwardRef>
</div>
<Label label="splitb" options={{...}} isToggleable={true} onToggle={[Function (anonymous)]} isSeriesHidden={false} totalSeriesCount={4} hiddenSeriesCount={0}>
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splitb\\nClick: isolate series\\nCtrl + click: hide series" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splitb; Click: isolate series\\nCtrl + click: hide series">
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splitb\\n\\nClick to show\\nCtrl + Click to hide" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splitb; Click: to show, Ctrl + Click: to hide">
splitb
</div>
</Label>
Expand All @@ -273,7 +273,7 @@ exports[`Legend #legendColorPicker should render colorPicker when color is click
</ForwardRef>
</div>
<Label label="splitc" options={{...}} isToggleable={true} onToggle={[Function (anonymous)]} isSeriesHidden={false} totalSeriesCount={4} hiddenSeriesCount={0}>
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splitc\\nClick: isolate series\\nCtrl + click: hide series" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splitc; Click: isolate series\\nCtrl + click: hide series">
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splitc\\n\\nClick to show\\nCtrl + Click to hide" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splitc; Click: to show, Ctrl + Click: to hide">
splitc
</div>
</Label>
Expand All @@ -295,7 +295,7 @@ exports[`Legend #legendColorPicker should render colorPicker when color is click
</ForwardRef>
</div>
<Label label="splitd" options={{...}} isToggleable={true} onToggle={[Function (anonymous)]} isSeriesHidden={false} totalSeriesCount={4} hiddenSeriesCount={0}>
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splitd\\nClick: isolate series\\nCtrl + click: hide series" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splitd; Click: isolate series\\nCtrl + click: hide series">
<div role="button" tabIndex={0} dir="ltr" className="echLegendItem__label echLegendItem__label--clickable echLegendItem__label--singleline" title="splitd\\n\\nClick to show\\nCtrl + Click to hide" onClick={[Function (anonymous)]} onKeyDown={[Function (anonymous)]} aria-pressed={false} style={{...}} aria-label="splitd; Click: to show, Ctrl + Click: to hide">
splitd
</div>
</Label>
Expand Down
42 changes: 25 additions & 17 deletions packages/charts/src/components/legend/label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,31 +24,41 @@ interface LabelProps {

const isAppleDevice = typeof window !== 'undefined' && /Mac|iPhone|iPad/.test(window.navigator.userAgent);

const modifierKey = isAppleDevice ? '⌘ (Command)' : 'Ctrl';
const isolateSeriesMessage = 'isolate series';
const showAllSeriesMessage = 'show all series';
const showSeriesMessage = 'show series';
const hideSeriesMessage = 'hide series';
const modifierKey = isAppleDevice ? '⌘' : 'Ctrl';
const showAllSeriesMessage = 'to show all';
const showSeriesMessage = 'to show';
const hideSeriesMessage = 'to hide';

function getInteractivityTitle(isSeriesVisible: boolean, hiddenSeries: number, allSeries: number) {
if (isSeriesVisible) {
if (allSeries - hiddenSeries === 1) {
return `
Click: ${showAllSeriesMessage}
${modifierKey} + click: ${hideSeriesMessage}`;
Click ${showAllSeriesMessage}
${modifierKey} + Click ${hideSeriesMessage}`;
}
if (hiddenSeries > 0) {
return `
Click: ${hideSeriesMessage}
${modifierKey} + click: ${hideSeriesMessage}`;
Click ${hideSeriesMessage}`;
}
return `
Click: ${isolateSeriesMessage}
${modifierKey} + click: ${hideSeriesMessage}`;
Click ${showSeriesMessage}
${modifierKey} + Click ${hideSeriesMessage}`;
}
return `
Click: ${showSeriesMessage}
${modifierKey} + click: ${showSeriesMessage}`;
Click ${showSeriesMessage}`;
}

function getInteractivityAriaLabel(isSeriesVisible: boolean, hiddenSeries: number, allSeries: number) {
if (isSeriesVisible) {
if (allSeries - hiddenSeries === 1) {
return `Click: ${showAllSeriesMessage}, ${modifierKey} + Click: ${hideSeriesMessage}`;
}
if (hiddenSeries > 0) {
return `Click: ${hideSeriesMessage}, ${modifierKey} + Click: ${hideSeriesMessage}`;
}
return `Click: ${showSeriesMessage}, ${modifierKey} + Click: ${hideSeriesMessage}`;
}
return `Click: ${showSeriesMessage}, ${modifierKey} + Click: ${showSeriesMessage}`;
}

/**
Expand Down Expand Up @@ -79,8 +89,6 @@ export function Label({

const title = options.maxLines > 0 ? label : ''; // full text already visible

const interactionsGuidanceText = getInteractivityTitle(!isSeriesHidden, hiddenSeriesCount, totalSeriesCount);

return isToggleable ? (
// This div is required to allow multiline text truncation, all ARIA requirements are still met
// https://stackoverflow.com/questions/68673034/webkit-line-clamp-does-not-apply-to-buttons
Expand All @@ -89,12 +97,12 @@ export function Label({
tabIndex={0}
dir={dir}
className={className}
title={`${title}${interactionsGuidanceText}`}
title={`${title}\n${getInteractivityTitle(!isSeriesHidden, hiddenSeriesCount, totalSeriesCount)}`}
onClick={onClick}
onKeyDown={onKeyDown}
aria-pressed={isSeriesHidden}
style={clampStyles}
aria-label={`${label}; ${interactionsGuidanceText.replace('\n', '')}`} // put it in a single line
aria-label={`${label}; ${getInteractivityAriaLabel(!isSeriesHidden, hiddenSeriesCount, totalSeriesCount)}`}
>
{label}
</div>
Expand Down

0 comments on commit 27c04f4

Please sign in to comment.