Skip to content

Commit 4c74e5f

Browse files
[Misc] Cherry pick 11756-2 (#11833)
Co-authored-by: Armando DBG <bollain@gmail.com> (r11.8 → 11.8)
1 parent 534400e commit 4c74e5f

File tree

6 files changed

+239
-69
lines changed

6 files changed

+239
-69
lines changed

src/components/ModularComponents/CellBorders/CellBorders.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { getPresetButtonDOM } from '../Helpers/menuItems';
77
import ToggleElementButton from '../ToggleElementButton';
88
import setCellBorder from 'src/helpers/setCellBorder';
99
import getButtonDisplay from 'src/helpers/getBorderDisplayButton';
10+
import isBorderButtonActive from 'src/helpers/isBorderButtonActive';
1011
import selectors from 'selectors';
1112
import { useSelector } from 'react-redux';
1213
import PropTypes from 'prop-types';
@@ -17,6 +18,7 @@ const CellBorders = (props) => {
1718
const { isFlyoutItem, dataElement, disabled } = props;
1819
const selectedBorderStyleListOption = useSelector((state)=> selectors.getSelectedBorderStyleListOption(state));
1920
const selectedBorderColorOption = useSelector((state)=> selectors.getSelectedBorderColorOption(state));
21+
const isSingleCell = useSelector((state) => selectors.getIsSingleCell(state));
2022

2123
const { t } = useTranslation();
2224

@@ -58,6 +60,7 @@ const CellBorders = (props) => {
5860
React.cloneElement(button, {
5961
key: button.props.dataElement,
6062
className: 'border-button',
63+
isActive: isSingleCell ? isBorderButtonActive(button.props.dataElement) : false
6164
})
6265
))}
6366
</div>

src/components/ModularComponents/CellBorders/CellBorders.stories.js

Lines changed: 111 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -5,50 +5,76 @@ import { Provider } from 'react-redux';
55
import { configureStore } from '@reduxjs/toolkit';
66
import rootReducer from 'src/redux/reducers/rootReducer';
77
import { within, expect } from 'storybook/test';
8-
import i18next from 'i18next';
8+
import { getTranslatedText } from 'helpers/testTranslationHelper';
99

1010
const createMockDocumentViewer = (getSelectedCellRange, cells) => () => ({
1111
getSpreadsheetEditorManager: () => ({
1212
getSelectedCellRange,
1313
getSelectedCells: () => cells,
1414
}),
1515
});
16-
const store = configureStore({ reducer: rootReducer });
1716
const originalCoreGetDocumentViewer = core.getDocumentViewer;
1817

19-
export default {
20-
title: 'ModularComponents/CellBorders',
21-
component: CellBorders,
22-
decorators: [
23-
(Story) => {
24-
core.getDocumentViewer = originalCoreGetDocumentViewer;
25-
return (
26-
<Provider store={store}>
27-
<Story />
28-
</Provider>
29-
);
30-
},
31-
],
18+
const mockConfigs = {
19+
multipleCells: {
20+
range: () => ({ firstRow: 0, lastRow: 1, firstColumn: 0, lastColumn: 1 }),
21+
cells: [
22+
{ getStyle: () => ({ getCellBorder: () => ({ style: 'Thin' }) }) },
23+
{ getStyle: () => ({ getCellBorder: () => ({}) }) }
24+
],
25+
isSingleCell: false
26+
},
27+
singleCell: {
28+
range: () => ({ firstRow: 1, lastRow: 1, firstColumn: 1, lastColumn: 1 }),
29+
cells: [{
30+
getStyle: () => ({
31+
getCellBorder: (side) => {
32+
return ['Right', 'Left'].includes(side)
33+
? { style: 'Thin' }
34+
: { style: 'None' };
35+
}
36+
})
37+
}],
38+
isSingleCell: true
39+
},
40+
noBorders: {
41+
range: () => ({ firstRow: 1, lastRow: 1, firstColumn: 1, lastColumn: 1 }),
42+
cells: [{
43+
getStyle: () => ({ getCellBorder: () => ({ style: 'None' }) })
44+
}],
45+
isSingleCell: true
46+
}
3247
};
3348

34-
const withMultipleCells = (Story) => {
35-
core.getDocumentViewer = createMockDocumentViewer(
36-
() => ({ firstRow: 0, lastRow: 1, firstColumn: 0, lastColumn: 1 }),
37-
[1, 2]
38-
);
39-
return <Story />;
49+
const createMockStore = (isSingleCell) => {
50+
return configureStore({
51+
reducer: rootReducer,
52+
preloadedState: {
53+
spreadsheetEditor: {
54+
cellProperties: {
55+
isSingleCell,
56+
styles: {}
57+
}
58+
}
59+
}
60+
});
4061
};
4162

42-
const withSingleCell = (Story) => {
43-
core.getDocumentViewer = createMockDocumentViewer(
44-
() => ({ firstRow: 1, lastRow: 1, firstColumn: 1, lastColumn: 1 }),
45-
[1]
46-
);
47-
return <Story />;
63+
const createMockDecorator = (configKey) => {
64+
const MockDecorator = (Story) => {
65+
const config = mockConfigs[configKey];
66+
core.getDocumentViewer = createMockDocumentViewer(config.range, config.cells);
67+
const mockStore = createMockStore(config.isSingleCell);
68+
return (
69+
<Provider store={mockStore}>
70+
<Story />
71+
</Provider>
72+
);
73+
};
74+
MockDecorator.displayName = `MockDecorator_${configKey}`;
75+
return MockDecorator;
4876
};
4977

50-
const Template = (args) => <CellBorders {...args} />;
51-
5278
const expectedButtons = {
5379
'none': 'alwaysShow',
5480
'all': 'showForMultipleCells',
@@ -66,52 +92,72 @@ const checkButtonVisibility = async (canvas, expectedMapping) => {
6692
await Promise.all(
6793
Object.entries(expectedMapping).map(async ([name, state]) => {
6894
const button = canvas.queryByRole('button', {
69-
name: i18next.t(`spreadsheetEditor.${name}`),
95+
name: getTranslatedText(`spreadsheetEditor.${name}`),
7096
});
71-
if (state === 'alwaysShow') {
72-
expect(button).toBeInTheDocument();
73-
} else {
74-
expect(button).toBeNull();
75-
}
97+
expect(button)[state === 'alwaysShow' ? 'toBeInTheDocument' : 'toBeNull']();
7698
})
7799
);
78100
};
79101

80-
export const FlyoutWithSingleCell = Template.bind({});
81-
FlyoutWithSingleCell.args = {
82-
isFlyoutItem: true,
83-
disabled: false,
84-
dataElement: 'cellBordersFlyoutSingle',
85-
};
86-
FlyoutWithSingleCell.decorators = [withSingleCell];
87-
FlyoutWithSingleCell.play = async ({ canvasElement }) => {
102+
const createPlayFunction = (checkAll = false) => async ({ canvasElement }) => {
88103
const canvas = within(canvasElement);
89104
try {
90-
await checkButtonVisibility(canvas, expectedButtons);
105+
if (checkAll) {
106+
await Promise.all(
107+
Object.keys(expectedButtons).map(async (name) => {
108+
const button = canvas.getByRole('button', {
109+
name: getTranslatedText(`spreadsheetEditor.${name}`),
110+
});
111+
expect(button).toBeInTheDocument();
112+
})
113+
);
114+
} else {
115+
await checkButtonVisibility(canvas, expectedButtons);
116+
}
91117
} finally {
92118
core.getDocumentViewer = originalCoreGetDocumentViewer;
93119
}
94120
};
95121

96-
export const FlyoutWithAllButtons = Template.bind({});
97-
FlyoutWithAllButtons.args = {
98-
isFlyoutItem: true,
99-
disabled: false,
100-
dataElement: 'cellBordersFlyoutAll',
101-
};
102-
FlyoutWithAllButtons.decorators = [withMultipleCells];
103-
FlyoutWithAllButtons.play = async ({ canvasElement }) => {
104-
const canvas = within(canvasElement);
105-
try {
106-
await Promise.all(
107-
Object.keys(expectedButtons).map(async (name) => {
108-
const button = canvas.getByRole('button', {
109-
name: i18next.t(`spreadsheetEditor.${name}`),
110-
});
111-
expect(button).toBeInTheDocument();
112-
})
113-
);
114-
} finally {
115-
core.getDocumentViewer = originalCoreGetDocumentViewer;
122+
const createStory = (dataElement, mockConfig, checkAll = false) => {
123+
const story = Template.bind({});
124+
story.args = {
125+
isFlyoutItem: true,
126+
disabled: false,
127+
dataElement,
128+
};
129+
story.decorators = [createMockDecorator(mockConfig)];
130+
story.play = createPlayFunction(checkAll);
131+
if (!checkAll) {
132+
story.parameters = window.storybook.disableRtlMode;
116133
}
117-
};
134+
return story;
135+
};
136+
137+
export default {
138+
title: 'ModularComponents/CellBorders',
139+
component: CellBorders,
140+
decorators: [
141+
(Story) => {
142+
core.getDocumentViewer = originalCoreGetDocumentViewer;
143+
return (
144+
<div>
145+
<style>
146+
{`
147+
.icon-grid .row .Button.active {
148+
box-shadow: inset 0 0 0 1px var(--blue-5);
149+
}
150+
`}
151+
</style>
152+
<Story />
153+
</div>
154+
);
155+
},
156+
],
157+
};
158+
159+
const Template = (args) => <CellBorders {...args} />;
160+
161+
export const FlyoutWithSingleCell = createStory('cellBordersFlyoutSingle', 'singleCell');
162+
export const FlyoutWithAllButtons = createStory('cellBordersFlyoutAll', 'multipleCells', true);
163+
export const FlyoutWithNoBorders = createStory('cellBordersFlyoutNoBorders', 'noBorders');

src/components/ModularComponents/Flyout/SpreadsheetFlyouts.stories.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ const createMockDocumentViewer = (getSelectedCellRange, cells) => () => ({
2020
}),
2121
});
2222

23+
const mockReturnRange = () => ({ firstRow: 0, lastRow: 1, firstColumn: 0, lastColumn: 1 });
24+
const mockCells = [{ getStyle: () => ({ getCellBorder: () => ({ style: 'None' }) }) }, {}];
25+
2326
const createInitialState = (activeFlyout, openElement) => ({
2427
...oePartialState,
2528
viewer: {
@@ -118,10 +121,7 @@ const WithResetCoreViewer = ({ children }) => {
118121

119122
const withStoreAndMockedCore = (activeFlyout, openElement) => {
120123
const decorator = (Story) => {
121-
core.getDocumentViewer = createMockDocumentViewer(
122-
() => ({ firstRow: 0, lastRow: 1, firstColumn: 0, lastColumn: 1 }),
123-
[1, 2]
124-
);
124+
core.getDocumentViewer = createMockDocumentViewer(mockReturnRange, mockCells);
125125
const store = createStore(activeFlyout, openElement);
126126
return (
127127
<Provider store={store}>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import core from 'core';
2+
3+
function getSelectedCellStyle() {
4+
if (!core.getDocumentViewer()) {
5+
return;
6+
}
7+
const spreadsheetEditorManager = core.getDocumentViewer().getSpreadsheetEditorManager();
8+
const selectedCells = spreadsheetEditorManager.getSelectedCells();
9+
if (selectedCells.length === 0) {
10+
return null;
11+
}
12+
13+
const style = selectedCells[0].getStyle();
14+
return style;
15+
}
16+
17+
export default getSelectedCellStyle;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import getSelectedCellStyle from './getSelectedCellStyle';
2+
3+
const isBorderButtonActive = (borderButton) => {
4+
const isSelectingMultipleCells = ['Inside', 'Vertical', 'Horizontal', 'All'].includes(borderButton);
5+
if (isSelectingMultipleCells) {
6+
return false;
7+
}
8+
const cellStyle = getSelectedCellStyle();
9+
const borderTopStyle = cellStyle?.getCellBorder('Top');
10+
const borderLeftStyle = cellStyle?.getCellBorder('Left');
11+
const borderBottomStyle = cellStyle?.getCellBorder('Bottom');
12+
const borderRightStyle = cellStyle?.getCellBorder('Right');
13+
14+
const isUndefinedBorders = !borderTopStyle && !borderLeftStyle && !borderBottomStyle && !borderRightStyle;
15+
const isRemovedBorders = [borderTopStyle, borderLeftStyle, borderBottomStyle, borderRightStyle].every((border) => border?.style === 'None');
16+
if (isUndefinedBorders || isRemovedBorders) {
17+
return borderButton === 'None';
18+
}
19+
const isHavingStylesOnAllBorders = [borderTopStyle, borderLeftStyle, borderBottomStyle, borderRightStyle].every((border) => border && border.style !== 'None');
20+
if (isHavingStylesOnAllBorders) {
21+
return borderButton === 'Outside';
22+
}
23+
24+
const borderStyleMap = {
25+
'Left': borderLeftStyle,
26+
'Right': borderRightStyle,
27+
'Top': borderTopStyle,
28+
'Bottom': borderBottomStyle,
29+
};
30+
31+
const borderStyle = borderStyleMap[borderButton];
32+
const isBorderStyleActive = borderStyle?.style !== 'None' && Object.keys(borderStyleMap).includes(borderButton);
33+
return isBorderStyleActive;
34+
};
35+
36+
export default isBorderButtonActive;
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import isBorderButtonActive from './isBorderButtonActive';
2+
import getSelectedCellStyle from './getSelectedCellStyle';
3+
4+
jest.mock('./getSelectedCellStyle');
5+
6+
describe('isBorderButtonActive', () => {
7+
let mockGetSelectedCellStyle;
8+
let mockGetCellBorder;
9+
10+
beforeEach(() => {
11+
mockGetCellBorder = jest.fn();
12+
mockGetSelectedCellStyle = jest.fn(() => ({
13+
getCellBorder: mockGetCellBorder,
14+
}));
15+
getSelectedCellStyle.mockImplementation(mockGetSelectedCellStyle);
16+
});
17+
18+
afterEach(() => {
19+
jest.clearAllMocks();
20+
});
21+
22+
describe('when checking border sides', () => {
23+
it('returns true for "Left" when left border has style other than "None"', () => {
24+
mockGetCellBorder.mockImplementation((side) => {
25+
if (side === 'Left') {
26+
return { style: 'Thin' };
27+
}
28+
return undefined;
29+
});
30+
31+
const state = isBorderButtonActive('Left');
32+
33+
expect(state).toBe(true);
34+
});
35+
36+
it('returns false for "Left" when left border has style "None"', () => {
37+
mockGetCellBorder.mockImplementation((side) => {
38+
if (side === 'Left') {
39+
return { style: 'None' };
40+
}
41+
return undefined;
42+
});
43+
44+
const state = isBorderButtonActive('Left');
45+
46+
expect(state).toBe(false);
47+
});
48+
});
49+
50+
describe('when getSelectedCellStyle returns null', () => {
51+
beforeEach(() => {
52+
getSelectedCellStyle.mockReturnValue(null);
53+
});
54+
55+
it('returns true for "None" button', () => {
56+
const state = isBorderButtonActive('None');
57+
expect(state).toBe(true);
58+
});
59+
60+
it('returns false for multiple cell selection buttons', () => {
61+
expect(isBorderButtonActive('Outside')).toBe(false);
62+
expect(isBorderButtonActive('Inside')).toBe(false);
63+
expect(isBorderButtonActive('Vertical')).toBe(false);
64+
expect(isBorderButtonActive('Horizontal')).toBe(false);
65+
expect(isBorderButtonActive('All')).toBe(false);
66+
});
67+
});
68+
});

0 commit comments

Comments
 (0)