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

New Legend Widget #837

Merged
merged 69 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from 65 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
ebe5239
WIP new legend
juandjara Feb 2, 2024
b345650
finish icon buttons with tooltips
juandjara Feb 2, 2024
c264e1c
legend helper text
juandjara Feb 2, 2024
5250f55
extract legend width constant
juandjara Feb 2, 2024
0499442
conditional overflow tooltip component
juandjara Feb 2, 2024
07268b0
sticky backgrounds
juandjara Feb 5, 2024
28af075
remove unnecesary prop passing in favor of unmountOnExit
juandjara Feb 5, 2024
6f0b7c6
split new legend into multiple files
juandjara Feb 5, 2024
4b78784
disable opacity button when layer is not visible
juandjara Feb 5, 2024
9bb1d39
first step of multi-variable legend
juandjara Feb 5, 2024
c89a5b9
show opacity control only when visible
juandjara Feb 5, 2024
94cd51b
add custom legend types config and improve multi legend support
juandjara Feb 6, 2024
f1d26f1
extract opacity component
juandjara Feb 6, 2024
f58780c
rename types
juandjara Feb 6, 2024
aed0d6b
rename legendItem to legendLayer and split components more
juandjara Feb 6, 2024
0f29e5d
option selector WIP
juandjara Feb 6, 2024
a09426e
finish layer selector
juandjara Feb 6, 2024
4e3b78d
translate visibility button
juandjara Feb 6, 2024
d84416a
fix import
juandjara Feb 6, 2024
b2491d7
translate more layers
juandjara Feb 7, 2024
d062ecc
translate all labels
juandjara Feb 7, 2024
dee933e
extract opacity icon
juandjara Feb 7, 2024
b1245ae
add more styles
juandjara Feb 7, 2024
eed96c2
legend attr subtitles
juandjara Feb 7, 2024
45abf19
complete legend types typing
juandjara Feb 7, 2024
87a9b2a
update individual legend styles
juandjara Feb 7, 2024
1ccb75c
extract list styles
juandjara Feb 8, 2024
674a06e
add min max configurable for proportion legend
juandjara Feb 8, 2024
d38a420
add default min max config for legend ramp
juandjara Feb 8, 2024
17c7aa5
wip mobile menu
juandjara Feb 8, 2024
f5cbccb
update react imports
juandjara Feb 8, 2024
53b05b6
fix the weirdest import bug ever
juandjara Feb 8, 2024
746e09a
finish mobile config
juandjara Feb 8, 2024
16491d3
fix typography import
juandjara Feb 8, 2024
f18e63f
refactor legend proportion
juandjara Feb 8, 2024
885114b
use typography atom component
juandjara Feb 8, 2024
45fa551
update jsdocs
juandjara Feb 8, 2024
04aa1c8
update all legends finished, and tests passing
juandjara Feb 8, 2024
b2f357a
remove console log
juandjara Feb 8, 2024
a29a702
everything done on widget, storybooks and tests
juandjara Feb 9, 2024
a9de216
rename storybook
juandjara Feb 9, 2024
d80115f
rename all new-legend components to replace old legend folder
juandjara Feb 9, 2024
a0b047e
WIP move sx to styled components
juandjara Feb 9, 2024
51f127e
more styled components
juandjara Feb 9, 2024
8d44ee8
fix styled components
juandjara Feb 9, 2024
49c4e4d
more styled components refactor
juandjara Feb 9, 2024
e9ccae4
remove unused param
juandjara Feb 9, 2024
062481b
update default max zoom
juandjara Feb 9, 2024
803cbe2
edge cases for empty and single layers
juandjara Feb 12, 2024
9b41b6d
add back null check
juandjara Feb 12, 2024
ce6fb17
fix hidden layer case
juandjara Feb 12, 2024
4a4812a
add sx prop to types
juandjara Feb 13, 2024
6715362
fix isSingle check
juandjara Mar 7, 2024
0d4f31a
fix root style
juandjara Mar 7, 2024
3bfaabd
fix number input styles for webkit
juandjara Mar 7, 2024
5df38ff
fix legendRamp
juandjara Mar 7, 2024
7c7249f
fix categories legend
juandjara Mar 7, 2024
0e9a1dc
change opacity show condition
juandjara Mar 7, 2024
8cc860a
fix list accesibility
juandjara Mar 8, 2024
6acf3e2
add name to legend select
juandjara Mar 8, 2024
7b0575c
remove single layer case
juandjara Mar 11, 2024
2037f04
add id translations from @gandeszahara
juandjara Mar 12, 2024
3c12b7a
remove unused keys
juandjara Mar 12, 2024
04e825c
remove single legend test
juandjara Mar 12, 2024
2d947d6
fix selection ev on LegendWidget
juandjara Mar 14, 2024
a12749b
refactor styles for sx, box and styled-components
juandjara Mar 18, 2024
d4ebc64
replace mui typography with project category
juandjara Mar 18, 2024
e9089a0
fix legend categories story
juandjara Mar 18, 2024
1bd89d4
Merge branch 'master' of github.com:CartoDB/carto-react into feature/…
padawannn Apr 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 64 additions & 90 deletions packages/react-ui/__tests__/widgets/LegendWidgetUI.test.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,25 @@
import React from 'react';
import Typography from '../../src/components/atoms/Typography';
import LegendWidgetUI from '../../src/widgets/legend/LegendWidgetUI';
import { fireEvent, render, screen } from '../widgets/utils/testUtils';

const CUSTOM_CHILDREN = <Typography>Legend custom</Typography>;

const MY_CUSTOM_LEGEND_KEY = 'my-custom-legend';

const LAYER_OPTIONS = {
PALETTE_SELECTOR: 'PALETTE_SELECTOR'
};

const LAYER_OPTIONS_COMPONENTS = {
[LAYER_OPTIONS.PALETTE_SELECTOR]: PaletteSelector
};

function PaletteSelector() {
return <p>PaletteSelector</p>;
}

describe('LegendWidgetUI', () => {
const DATA = [
{
// 0
id: 'category',
title: 'Category Layer',
visible: true,
helperText: 'lorem',
legend: {
type: 'category',
note: 'lorem',
colors: ['#000', '#00F', '#0F0'],
labels: ['Category 1', 'Category 2', 'Category 3']
}
},
{
// 1
id: 'icon',
title: 'Icon Layer',
visible: true,
Expand All @@ -47,6 +34,7 @@ describe('LegendWidgetUI', () => {
}
},
{
// 2
id: 'bins',
title: 'Ramp Layer',
visible: true,
Expand All @@ -57,6 +45,7 @@ describe('LegendWidgetUI', () => {
}
},
{
// 3
id: 'continuous',
title: 'Ramp Layer',
visible: true,
Expand All @@ -67,6 +56,7 @@ describe('LegendWidgetUI', () => {
}
},
{
// 4
id: 'proportion',
title: 'Proportion Layer',
visible: true,
Expand All @@ -76,14 +66,7 @@ describe('LegendWidgetUI', () => {
}
},
{
id: 'custom',
title: 'Single Layer',
visible: true,
legend: {
children: CUSTOM_CHILDREN
}
},
{
// 5
id: 'custom_key',
title: 'Single Layer',
visible: true,
Expand All @@ -95,33 +78,10 @@ describe('LegendWidgetUI', () => {
colors: ['#000', '#00F', '#0F0'],
labels: ['Category 1', 'Category 2', 'Category 3']
}
},
{
id: 'custom_children',
title: 'Single Layer',
visible: true,
showOpacityControl: true,
opacity: 0.6,
legend: {
children: CUSTOM_CHILDREN
}
},
{
id: 'palette',
title: 'Store types',
visible: true,
options: [LAYER_OPTIONS.PALETTE_SELECTOR],
legend: {
children: CUSTOM_CHILDREN
}
}
];
const Widget = (props) => <LegendWidgetUI {...props} />;

test('single legend', () => {
render(<Widget layers={[DATA[0]]}></Widget>);
expect(screen.queryByText('Layers')).not.toBeInTheDocument();
});
const Widget = (props) => <LegendWidgetUI {...props} />;

test('multiple legends', () => {
render(<Widget layers={DATA}></Widget>);
Expand All @@ -131,10 +91,33 @@ describe('LegendWidgetUI', () => {

test('multiple legends with collapsed as true', () => {
render(<Widget layers={DATA} collapsed={true}></Widget>);
expect(screen.queryByText('Layers')).toBeInTheDocument();
// expanded legend toggle
expect(screen.queryByText('Layers')).not.toBeInTheDocument();
// collapsed legend toggle
expect(screen.queryByLabelText('Layers')).toBeInTheDocument();
expect(screen.queryByTestId('categories-legend')).not.toBeInTheDocument();
});

test('layer with no legend is not shown on widget', () => {
const layers = [
{
id: 'test-layer-no-legend',
title: 'Test layer hidden',
visible: true
},
{
id: 'test-layer-no-legend-2',
title: 'Test layer shown',
visible: true,
legend: {}
}
];
render(<Widget layers={layers}></Widget>);
expect(screen.queryByText('Layers')).toBeInTheDocument();
expect(screen.queryByText('Test layer hidden')).not.toBeInTheDocument();
expect(screen.queryByText('Test layer shown')).toBeInTheDocument();
});

test('Category legend', () => {
render(<Widget layers={[DATA[0]]}></Widget>);
expect(screen.getByTestId('categories-legend')).toBeInTheDocument();
Expand All @@ -160,11 +143,6 @@ describe('LegendWidgetUI', () => {
expect(screen.getByTestId('proportion-legend')).toBeInTheDocument();
});

test('Custom legend', () => {
render(<Widget layers={[DATA[5]]}></Widget>);
expect(screen.getByText('Legend custom')).toBeInTheDocument();
});

test('Empty legend', () => {
const EMPTY_LAYER = { id: 'empty', title: 'Empty Layer', legend: {} };
render(<Widget layers={[EMPTY_LAYER]}></Widget>);
Expand All @@ -184,42 +162,53 @@ describe('LegendWidgetUI', () => {
test('with custom legend types', () => {
const MyCustomLegendComponent = jest.fn();
MyCustomLegendComponent.mockReturnValue(<p>Test</p>);

render(
<Widget
layers={[DATA[6]]}
layers={[DATA[5]]}
customLegendTypes={{ [MY_CUSTOM_LEGEND_KEY]: MyCustomLegendComponent }}
></Widget>
);

expect(MyCustomLegendComponent).toHaveBeenCalled();
expect(MyCustomLegendComponent).toHaveBeenCalledWith(
{ layer: DATA[6], legend: DATA[6].legend },
{ layer: DATA[5], legend: DATA[5].legend },
{}
);
expect(screen.getByText('Test')).toBeInTheDocument();
});

test('legend with opacity control', async () => {
const legendConfig = DATA[7];
const legendConfig = {
id: 'test-opacity-control',
title: 'Test opacity control',
visible: true,
showOpacityControl: true,
opacity: 0.8,
legend: {}
};
const onChangeOpacity = jest.fn();
const container = render(
<Widget layers={[legendConfig]} onChangeOpacity={onChangeOpacity}></Widget>
);
const layerOptionsBtn = await screen.findByLabelText('Layer options');
expect(layerOptionsBtn).toBeInTheDocument();
layerOptionsBtn.click();
expect(screen.getByText('Opacity')).toBeInTheDocument();

const toggleButton = screen.getByRole('button', { name: 'Opacity' });
expect(toggleButton).toBeInTheDocument();
toggleButton.click();

const opacitySelectorInput = container.getByTestId('opacity-slider');
expect(opacitySelectorInput.value).toBe('' + legendConfig.opacity * 100);
expect(opacitySelectorInput).toBeInTheDocument();

expect(opacitySelectorInput.value).toBe(String(legendConfig.opacity * 100));

fireEvent.change(opacitySelectorInput, { target: { value: '50' } });
fireEvent.change(opacitySelectorInput, { target: { value: 50 } });

expect(onChangeOpacity).toHaveBeenCalledTimes(1);
expect(onChangeOpacity).toHaveBeenCalledWith({ id: legendConfig.id, opacity: 0.5 });
});

test('should manage legend collapsed state correctly', () => {
let legendConfig = { ...DATA[7], legend: { ...DATA[7].legend, collapsed: true } };
let legendConfig = { ...DATA[0], collapsed: true };
const onChangeLegendRowCollapsed = jest.fn();

const { rerender } = render(
Expand All @@ -229,48 +218,33 @@ describe('LegendWidgetUI', () => {
></Widget>
);

expect(screen.queryByText('Legend custom')).not.toBeInTheDocument();
expect(screen.queryByTestId('legend-layer-variable-list')).not.toBeInTheDocument();

const layerOptionsBtn = screen.getByText('Single Layer');
expect(layerOptionsBtn).toBeInTheDocument();
layerOptionsBtn.click();
const toggleButton = screen.getByRole('button', { name: 'Expand layer' });
expect(toggleButton).toBeInTheDocument();
toggleButton.click();

expect(onChangeLegendRowCollapsed).toHaveBeenCalledTimes(1);
expect(onChangeLegendRowCollapsed).toHaveBeenCalledWith({
id: legendConfig.id,
collapsed: false
});

legendConfig = { ...DATA[7], legend: { ...DATA[7].legend, collapsed: false } };
legendConfig = { ...DATA[0], collapsed: false };

rerender(
<Widget
layers={[legendConfig]}
onChangeLegendRowCollapsed={onChangeLegendRowCollapsed}
></Widget>
);

expect(screen.getByText('Legend custom')).toBeInTheDocument();
expect(screen.getByTestId('legend-layer-variable-list')).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Collapse layer' })).toBeInTheDocument();
});

test('with custom layer options', async () => {
const layer = DATA[8];
render(
<Widget layers={[layer]} customLayerOptions={LAYER_OPTIONS_COMPONENTS}></Widget>
);
const layerOptionsBtn = await screen.findByLabelText('Layer options');
expect(layerOptionsBtn).toBeInTheDocument();
layerOptionsBtn.click();
expect(screen.getByText('PaletteSelector')).toBeInTheDocument();
});

test('with custom layer options - unknown option', async () => {
const layer = { ...DATA[8], options: ['unknown'] };
render(
<Widget layers={[layer]} customLayerOptions={LAYER_OPTIONS_COMPONENTS}></Widget>
);
const layerOptionsBtn = await screen.findByLabelText('Layer options');
expect(layerOptionsBtn).toBeInTheDocument();
layerOptionsBtn.click();
expect(screen.getByText('Unknown layer option')).toBeInTheDocument();
test('helper text', () => {
render(<Widget layers={[{ ...DATA[0], helperText: 'helperText' }]}></Widget>);
expect(screen.getByText('helperText')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { render, screen } from '../utils/testUtils';
import LegendCategories from '../../../src/widgets/legend/LegendCategories';
import LegendCategories from '../../../src/widgets/legend/legend-types/LegendCategories';
import { getPalette } from '../../../src/utils/palette';
import { hexToRgb } from '@mui/material';

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,19 +1,37 @@
import React from 'react';
import { render, screen } from '../../widgets/utils/testUtils';
import LegendProportion from '../../../src/widgets/legend/LegendProportion';
import LegendProportion from '../../../src/widgets/legend/legend-types/LegendProportion';
import { IntlProvider } from 'react-intl';

const DEFAULT_LEGEND = {
labels: ['0', '200']
};

describe('LegendProportion', () => {
test('renders correctly', () => {
render(<LegendProportion legend={DEFAULT_LEGEND} />);
render(
<IntlProvider locale='en'>
<LegendProportion legend={DEFAULT_LEGEND} />
</IntlProvider>
);
expect(screen.queryByText('Max: 200')).toBeInTheDocument();
expect(screen.queryByText('150')).toBeInTheDocument();
expect(screen.queryByText('50')).toBeInTheDocument();
expect(screen.queryByText('Min: 0')).toBeInTheDocument();
});
test('renders correctly without min and max', () => {
render(
<IntlProvider locale='en'>
<LegendProportion legend={{ ...DEFAULT_LEGEND, showMinMax: false }} />
</IntlProvider>
);
expect(screen.queryByText('Max')).not.toBeInTheDocument();
expect(screen.queryByText('Min')).not.toBeInTheDocument();
expect(screen.queryByText('200')).toBeInTheDocument();
expect(screen.queryByText('150')).toBeInTheDocument();
expect(screen.queryByText('50')).toBeInTheDocument();
expect(screen.queryByText('0')).toBeInTheDocument();
});
test('renders error if neither labels is defined', () => {
render(<LegendProportion legend={{}} />);
expect(
Expand Down
Loading
Loading