Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(legend): improve tooltip wording #2439

Merged
merged 11 commits into from
Jun 3, 2024
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