Skip to content

Commit

Permalink
Merge pull request #299 from adobe/legendKeys
Browse files Browse the repository at this point in the history
fix: hideShow support for legend keys
  • Loading branch information
marshallpete committed May 10, 2024
2 parents 3ab3dc7 + a808070 commit 5445b11
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 38 deletions.
12 changes: 2 additions & 10 deletions src/specBuilder/legend/legendSpecBuilder.test.ts
Expand Up @@ -45,21 +45,13 @@ const defaultSpec: Spec = {
};

const colorEncoding = { signal: `scale('${COLOR_SCALE}', data('legend0Aggregate')[datum.index].${DEFAULT_COLOR})` };
const hiddenSeriesEncoding = {
test: 'indexof(hiddenSeries, datum.value) !== -1',
value: 'rgb(213, 213, 213)',
};

const defaultSymbolUpdateEncodings: SymbolEncodeEntry = {
fill: [hiddenSeriesEncoding, colorEncoding],
stroke: [hiddenSeriesEncoding, colorEncoding],
fill: [colorEncoding],
stroke: [colorEncoding],
};
const hiddenSeriesLabelUpdateEncoding = {
fill: [
{
test: 'indexof(hiddenSeries, datum.value) !== -1',
value: 'rgb(144, 144, 144)',
},
{
value: 'rgb(70, 70, 70)',
},
Expand Down
48 changes: 32 additions & 16 deletions src/specBuilder/legend/legendUtils.test.ts
Expand Up @@ -9,24 +9,21 @@
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
import { DEFAULT_COLOR_SCHEME } from '@constants';
import { DEFAULT_COLOR_SCHEME, FILTERED_TABLE } from '@constants';
import { spectrumColors } from '@themes';

import { defaultLegendProps } from './legendTestUtils';
import { getShowHideEncodings, getSymbolEncodings, getSymbolType, mergeLegendEncodings } from './legendUtils';
import {
getHiddenSeriesColorRule,
getShowHideEncodings,
getSymbolEncodings,
getSymbolType,
mergeLegendEncodings,
} from './legendUtils';

const hiddenSeriesEncoding = {
test: 'indexof(hiddenSeries, datum.value) !== -1',
value: 'rgb(213, 213, 213)',
};

const hiddenSeriesLabelUpdateEncoding = {
const labelUpdateEncoding = {
update: {
fill: [
{
test: 'indexof(hiddenSeries, datum.value) !== -1',
value: 'rgb(144, 144, 144)',
},
{
value: 'rgb(70, 70, 70)',
},
Expand All @@ -52,8 +49,8 @@ describe('getSymbolEncodings()', () => {
symbols: {
enter: {},
update: {
fill: [hiddenSeriesEncoding, { value: spectrumColors.light['categorical-100'] }],
stroke: [hiddenSeriesEncoding, { value: spectrumColors.light['categorical-100'] }],
fill: [{ value: spectrumColors.light['categorical-100'] }],
stroke: [{ value: spectrumColors.light['categorical-100'] }],
},
},
});
Expand All @@ -68,7 +65,7 @@ describe('getShowHideEncodings()', () => {
isToggleable: false,
onClick: undefined,
})
).toEqual({ labels: hiddenSeriesLabelUpdateEncoding });
).toEqual({ labels: labelUpdateEncoding });
});
test('should return encodings if isToggleable', () => {
const encoding = getShowHideEncodings({ ...defaultLegendProps, isToggleable: true });
Expand All @@ -84,7 +81,7 @@ describe('getShowHideEncodings()', () => {
const encoding = getShowHideEncodings({ ...defaultLegendProps, onClick: () => {} });
expect(encoding).toHaveProperty('entries');
expect(encoding).toHaveProperty('labels');
expect(encoding.labels).toStrictEqual(hiddenSeriesLabelUpdateEncoding);
expect(encoding.labels).toStrictEqual(labelUpdateEncoding);
});
});

Expand Down Expand Up @@ -142,3 +139,22 @@ describe('getSymbolType()', () => {
expect(getSymbolType('series')).toStrictEqual('circle');
});
});

describe('getHiddenSeriesColorRule()', () => {
test('should return empty array if not toggleable and no hiddenSeries', () => {
expect(getHiddenSeriesColorRule(defaultLegendProps, 'gray-300')).toEqual([]);
});

test('should use filteredTable if there are keys', () => {
const colorRules = getHiddenSeriesColorRule(
{ ...defaultLegendProps, isToggleable: true, keys: ['key1'] },
'gray-300'
);
expect(colorRules[0].test).toContain(FILTERED_TABLE);
});

test('should look at hiddenSeries if there are not any keys', () => {
const colorRules = getHiddenSeriesColorRule({ ...defaultLegendProps, isToggleable: true }, 'gray-300');
expect(colorRules[0].test).toContain('hiddenSeries');
});
});
16 changes: 12 additions & 4 deletions src/specBuilder/legend/legendUtils.ts
Expand Up @@ -12,6 +12,7 @@
import {
COLOR_SCALE,
DEFAULT_OPACITY_RULE,
FILTERED_TABLE,
HIGHLIGHTED_GROUP,
HIGHLIGHTED_SERIES,
HIGHLIGHT_CONTRAST_RATIO,
Expand Down Expand Up @@ -278,13 +279,20 @@ const getSymbolFacetEncoding = <T>({
};

export const getHiddenSeriesColorRule = (
{ colorScheme, hiddenSeries, isToggleable, keys }: LegendSpecProps,
{ colorScheme, hiddenSeries, isToggleable, keys, name }: LegendSpecProps,
colorValue: ColorValueV6
): ({
test?: string;
} & ColorValueRef)[] => {
// if the legend doesn't support hide/show or if it has custom keys, don't add the hidden series color rule
if ((!isToggleable && !hiddenSeries) || keys) return [];
if (!isToggleable && !hiddenSeries.length) return [];
if (keys?.length) {
return [
{
test: `indexof(pluck(data('${FILTERED_TABLE}'), '${name}_groupId'), datum.value) === -1`,
value: getColorValue(colorValue, colorScheme),
},
];
}
return [{ test: 'indexof(hiddenSeries, datum.value) !== -1', value: getColorValue(colorValue, colorScheme) }];
};

Expand All @@ -297,7 +305,7 @@ export const getShowHideEncodings = (props: LegendSpecProps): LegendEncode => {
const { colorScheme, hiddenSeries, isToggleable, keys, name, onClick } = props;
let hiddenSeriesEncode: LegendEncode = {};
// if the legend supports hide/show and doesn't have custom keys, add the hidden series encodings
if ((hiddenSeries || isToggleable) && !keys) {
if (hiddenSeries || isToggleable) {
hiddenSeriesEncode = {
labels: {
update: {
Expand Down
25 changes: 23 additions & 2 deletions src/specBuilder/signal/signalSpecBuilder.test.ts
Expand Up @@ -9,15 +9,19 @@
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
import { HIGHLIGHTED_ITEM, HIGHLIGHTED_SERIES } from '@constants';
import { FILTERED_TABLE, HIGHLIGHTED_ITEM, HIGHLIGHTED_SERIES } from '@constants';
import {
defaultHighlightedItemSignal,
defaultHighlightedSeriesSignal,
defaultSignals,
} from '@specBuilder/specTestUtils';
import { Signal } from 'vega';

import { addHighlightedItemSignalEvents, addHighlightedSeriesSignalEvents } from './signalSpecBuilder';
import {
addHighlightedItemSignalEvents,
addHighlightedSeriesSignalEvents,
getHighlightSignalUpdateExpression,
} from './signalSpecBuilder';

describe('signalSpecBuilder', () => {
let signals: Signal[];
Expand Down Expand Up @@ -94,4 +98,21 @@ describe('signalSpecBuilder', () => {
expect(signals[4].on).toBeUndefined();
});
});

describe('getHighlightSignalUpdateExpression()', () => {
test('should return basic rule if there is no method of hidding series', () => {
const update = getHighlightSignalUpdateExpression('legend0', false);
expect(update).toBe(`domain("legend0Entries")[datum.index]`);
});

test('should reference filteredTable if there are keys', () => {
const update = getHighlightSignalUpdateExpression('legend0', true, ['key1', 'key2']);
expect(update).toContain(FILTERED_TABLE);
});

test('should referende hiddenSeries if there are not keys', () => {
const update = getHighlightSignalUpdateExpression('legend0', true, []);
expect(update).toContain('hiddenSeries');
});
});
});
31 changes: 25 additions & 6 deletions src/specBuilder/signal/signalSpecBuilder.ts
Expand Up @@ -9,7 +9,14 @@
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
import { HIGHLIGHTED_GROUP, HIGHLIGHTED_ITEM, HIGHLIGHTED_SERIES, MARK_ID, SERIES_ID } from '@constants';
import {
FILTERED_TABLE,
HIGHLIGHTED_GROUP,
HIGHLIGHTED_ITEM,
HIGHLIGHTED_SERIES,
MARK_ID,
SERIES_ID,
} from '@constants';
import { Signal } from 'vega';

/**
Expand Down Expand Up @@ -58,19 +65,31 @@ export const addHighlighSignalLegendHoverEvents = (
if (highlightedItemSignal.on === undefined) {
highlightedItemSignal.on = [];
}
const hoveredSeries = `domain("${legendName}Entries")[datum.index]`;
const update = includeHiddenSeries
? `indexof(hiddenSeries, ${hoveredSeries}) === -1 ? ${hoveredSeries} : null`
: hoveredSeries;
highlightedItemSignal.on.push(
...[
{ events: `@${legendName}_legendEntry:mouseover`, update },
{
events: `@${legendName}_legendEntry:mouseover`,
update: getHighlightSignalUpdateExpression(legendName, includeHiddenSeries, keys),
},
{ events: `@${legendName}_legendEntry:mouseout`, update: 'null' },
]
);
}
};

export const getHighlightSignalUpdateExpression = (
legendName: string,
includeHiddenSeries: boolean,
keys?: string[]
) => {
const hoveredSeriesExpression = `domain("${legendName}Entries")[datum.index]`;
if (!includeHiddenSeries) return hoveredSeriesExpression;
if (keys?.length) {
return `indexof(pluck(data("${FILTERED_TABLE}"),"${legendName}_groupId"), ${hoveredSeriesExpression}) !== -1 ? ${hoveredSeriesExpression} : null`;
}
return `indexof(hiddenSeries, ${hoveredSeriesExpression}) === -1 ? ${hoveredSeriesExpression} : null`;
};

/**
* Returns the legendLabels series signal
*/
Expand Down

0 comments on commit 5445b11

Please sign in to comment.