Skip to content

Commit 0bdf528

Browse files
committed
Added the ability to turn on/off row expanders
1 parent 32762e7 commit 0bdf528

31 files changed

+220
-19
lines changed

docs/CONFIGURATION.md

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ lock_header_menu = False
2525
hide_header_menu = False
2626
hide_main_menu = False
2727
hide_column_menus = False
28+
hide_row_expanders = False
2829
enable_custom_filters = False
2930
enable_web_uploads = False
3031

dtale/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
HIDE_HEADER_MENU = False
2323
HIDE_MAIN_MENU = False
2424
HIDE_COLUMN_MENUS = False
25+
HIDE_ROW_EXPANDERS = False
2526
ENABLE_CUSTOM_FILTERS = False
2627
ENABLE_WEB_UPLOADS = False
2728

dtale/app.py

+3
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,8 @@ def show(data=None, data_loader=None, name=None, context_vars=None, **options):
722722
:param hide_column_menus: If true, this will hide the column menus from the screen
723723
:type hide_column_menus: bool, optional
724724
:param column_edit_options: The options to allow on the front-end when editing a cell for the columns specified
725+
:param hide_row_expanders: If true, this will hide the row expanders from the screen
726+
:type hide_row_expanders: bool, optional
725727
:type column_edit_options: dict, optional
726728
:param auto_hide_empty_columns: if True, then auto-hide any columns on the front-end that are comprised entirely of
727729
NaN values
@@ -830,6 +832,7 @@ def show(data=None, data_loader=None, name=None, context_vars=None, **options):
830832
hide_header_menu=final_options.get("hide_header_menu"),
831833
hide_main_menu=final_options.get("hide_main_menu"),
832834
hide_column_menus=final_options.get("hide_column_menus"),
835+
hide_row_expanders=final_options.get("hide_row_expanders"),
833836
enable_custom_filters=final_options.get("enable_custom_filters"),
834837
enable_web_uploads=final_options.get("enable_web_uploads"),
835838
main_title=final_options.get("main_title"),

dtale/config.py

+9
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ def load_app_settings(config):
9696
section="app",
9797
getter="getboolean",
9898
)
99+
hide_row_expanders = get_config_val(
100+
config,
101+
curr_app_settings,
102+
"hide_row_expanders",
103+
section="app",
104+
getter="getboolean",
105+
)
99106
lock_header_menu = get_config_val(
100107
config,
101108
curr_app_settings,
@@ -155,6 +162,7 @@ def load_app_settings(config):
155162
hide_header_menu=hide_header_menu,
156163
hide_main_menu=hide_main_menu,
157164
hide_column_menus=hide_column_menus,
165+
hide_row_expanders=hide_row_expanders,
158166
enable_custom_filters=enable_custom_filters,
159167
enable_web_uploads=enable_web_uploads,
160168
)
@@ -226,6 +234,7 @@ def build_show_options(options=None):
226234
hide_header_menu=None,
227235
hide_main_menu=None,
228236
hide_column_menus=None,
237+
hide_row_expanders=None,
229238
enable_custom_filters=None,
230239
enable_web_uploads=None,
231240
main_title=None,

dtale/global_state.py

+3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"hide_column_menus": False,
3535
"enable_custom_filters": False,
3636
"enable_web_uploads": False,
37+
"hide_row_expanders": False,
3738
}
3839

3940
AUTH_SETTINGS = {"active": False, "username": None, "password": None}
@@ -612,6 +613,8 @@ def set_app_settings(settings):
612613
instance_updates["hide_main_menu"] = settings.get("hide_main_menu")
613614
if settings.get("hide_column_menus") is not None:
614615
instance_updates["hide_column_menus"] = settings.get("hide_column_menus")
616+
if settings.get("hide_row_expanders") is not None:
617+
instance_updates["hide_row_expanders"] = settings.get("hide_row_expanders")
615618
if settings.get("enable_custom_filters") is not None:
616619
instance_updates["enable_custom_filters"] = settings.get(
617620
"enable_custom_filters"

dtale/templates/dtale/base.html

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
<input type="hidden" id="hide_header_menu" value="{{hide_header_menu}}" />
4545
<input type="hidden" id="hide_main_menu" value="{{hide_main_menu}}" />
4646
<input type="hidden" id="hide_column_menus" value="{{hide_column_menus}}" />
47+
<input type="hidden" id="hide_row_expanders" value="{{hide_row_expanders}}" />
4748
<input type="hidden" id="enable_custom_filters" value="{{enable_custom_filters}}" />
4849
<input type="hidden" id="enable_web_uploads" value="{{enable_web_uploads}}" />
4950
<input type="hidden" id="allow_cell_edits" value="{{allow_cell_edits}}" />

dtale/views.py

+8
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ def update_settings(self, **updates):
366366
* hide_header_menu - if true, this will hide the header menu from the screen
367367
* hide_main_menu - if true, this will hide the main menu from the screen
368368
* hide_column_menus - if true, this will hide the column menus from the screen
369+
* hide_row_expanders - if true, this will hide row expanders from the screen
369370
* enable_custom_filters - if True, allow users to specify custom filters from the UI using pandas.query strings
370371
* enable_web_uploads - if True, allow users to upload files using URLs from the UI
371372
@@ -938,6 +939,7 @@ def startup(
938939
hide_header_menu=None,
939940
hide_main_menu=None,
940941
hide_column_menus=None,
942+
hide_row_expanders=None,
941943
enable_custom_filters=None,
942944
enable_web_uploads=None,
943945
force_save=True,
@@ -1069,6 +1071,7 @@ def startup(
10691071
hide_header_menu=hide_header_menu,
10701072
hide_main_menu=hide_main_menu,
10711073
hide_column_menus=hide_column_menus,
1074+
hide_row_expanders=hide_row_expanders,
10721075
enable_custom_filters=enable_custom_filters,
10731076
enable_web_uploads=enable_web_uploads,
10741077
main_title=main_title,
@@ -1144,6 +1147,7 @@ def startup(
11441147
hide_header_menu=hide_header_menu,
11451148
hide_main_menu=hide_main_menu,
11461149
hide_column_menus=hide_column_menus,
1150+
hide_row_expanders=hide_row_expanders,
11471151
enable_custom_filters=enable_custom_filters,
11481152
enable_web_uploads=enable_web_uploads,
11491153
main_title=main_title,
@@ -1215,6 +1219,8 @@ def startup(
12151219
base_settings["hide_main_menu"] = hide_main_menu
12161220
if hide_column_menus is not None:
12171221
base_settings["hide_column_menus"] = hide_column_menus
1222+
if hide_row_expanders is not None:
1223+
base_settings["hide_row_expanders"] = hide_row_expanders
12181224
if enable_custom_filters is not None:
12191225
base_settings["enable_custom_filters"] = enable_custom_filters
12201226
if enable_web_uploads is not None:
@@ -1317,6 +1323,7 @@ def base_render_template(template, data_id, **kwargs):
13171323
hide_header_menu = global_state.load_flag(data_id, "hide_header_menu", False)
13181324
hide_main_menu = global_state.load_flag(data_id, "hide_main_menu", False)
13191325
hide_column_menus = global_state.load_flag(data_id, "hide_column_menus", False)
1326+
hide_row_expanders = global_state.load_flag(data_id, "hide_row_expanders", False)
13201327
main_title = global_state.load_flag(data_id, "main_title", None)
13211328
main_title_font = global_state.load_flag(data_id, "main_title_font", None)
13221329
enable_custom_filters = global_state.load_flag(
@@ -1331,6 +1338,7 @@ def base_render_template(template, data_id, **kwargs):
13311338
hide_header_menu=hide_header_menu,
13321339
hide_main_menu=hide_main_menu,
13331340
hide_column_menus=hide_column_menus,
1341+
hide_row_expanders=hide_row_expanders,
13341342
enable_custom_filters=enable_custom_filters,
13351343
enable_web_uploads=enable_web_uploads,
13361344
github_fork=github_fork,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { act, fireEvent, render } from '@testing-library/react';
2+
import * as React from 'react';
3+
import { Provider } from 'react-redux';
4+
import { Store } from 'redux';
5+
6+
import HideRowExpanders from '../../../dtale/menu/HideRowExpanders';
7+
import * as serverState from '../../../dtale/serverStateManagement';
8+
import reduxUtils from '../../redux-test-utils';
9+
import { buildInnerHTML } from '../../test-utils';
10+
11+
describe('HideRowExpanders tests', () => {
12+
let result: Element;
13+
let store: Store;
14+
let updateSettingsSpy: jest.SpyInstance;
15+
16+
const setupOption = async (settings = ''): Promise<void> => {
17+
store = reduxUtils.createDtaleStore();
18+
buildInnerHTML({ settings }, store);
19+
result = await act(() => {
20+
return render(
21+
<Provider store={store}>
22+
<HideRowExpanders />,
23+
</Provider>,
24+
{
25+
container: document.getElementById('content') ?? undefined,
26+
},
27+
).container;
28+
});
29+
};
30+
31+
beforeEach(() => {
32+
updateSettingsSpy = jest.spyOn(serverState, 'updateSettings');
33+
updateSettingsSpy.mockImplementation(() => undefined);
34+
});
35+
36+
afterEach(jest.resetAllMocks);
37+
38+
afterAll(jest.restoreAllMocks);
39+
40+
it('renders successfully with defaults', async () => {
41+
await setupOption();
42+
expect(result.getElementsByClassName('ico-check-box-outline-blank')).toHaveLength(1);
43+
});
44+
45+
it('renders successfully with specified value', async () => {
46+
await setupOption('{&quot;hide_row_expanders&quot;:&quot;True&quot;}');
47+
expect(result.getElementsByClassName('ico-check-box')).toHaveLength(1);
48+
});
49+
50+
it('handles changes to checkbox', async () => {
51+
await setupOption();
52+
await act(async () => {
53+
await fireEvent.click(result.getElementsByClassName('ico-check-box-outline-blank')[0]);
54+
});
55+
expect(updateSettingsSpy).toBeCalledTimes(1);
56+
expect(store.getState().settings).toEqual(expect.objectContaining({ hide_row_expanders: true }));
57+
await act(async () => {
58+
await fireEvent.click(result.getElementsByClassName('ico-check-box')[0]);
59+
});
60+
expect(updateSettingsSpy).toBeCalledTimes(2);
61+
expect(updateSettingsSpy.mock.calls[1][0]).toEqual({
62+
hide_row_expanders: false,
63+
});
64+
expect(store.getState().settings).toEqual(expect.objectContaining({ hide_row_expanders: false }));
65+
});
66+
});

frontend/static/__tests__/dtale/reduxGridUtils-test.ts

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ describe('reduxGridUtils', () => {
2323
hide_header_menu: false,
2424
hide_main_menu: false,
2525
hide_column_menus: false,
26+
hide_row_expanders: false,
2627
enable_custom_filters: false,
2728
};
2829
reduxUtils.handleReduxState(

frontend/static/__tests__/reducers/dtale-test.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ describe('reducer tests', () => {
2828
hideHeaderMenu: false,
2929
hideMainMenu: false,
3030
hideColumnMenus: false,
31+
hideRowExpanders: false,
3132
enableCustomFilters: false,
3233
enableWebUploads: false,
3334
hideDropRows: false,
@@ -54,6 +55,7 @@ describe('reducer tests', () => {
5455
hide_header_menu: false,
5556
hide_main_menu: false,
5657
hide_column_menus: false,
58+
hide_row_expanders: false,
5759
enable_custom_filters: false,
5860
},
5961
pythonVersion: null,

frontend/static/__tests__/test-utils.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ export const buildInnerHTML = (props: Record<string, string | undefined> = {}, s
9595
buildHidden('hide_header_menu', props.hideHeaderMenu ?? HIDE_SHUTDOWN),
9696
buildHidden('hide_main_menu', props.hideMainMenu ?? HIDE_SHUTDOWN),
9797
buildHidden('hide_column_menus', props.hideColumnMenus ?? HIDE_SHUTDOWN),
98+
buildHidden('hide_row_expanders', props.hideRowExpanders ?? HIDE_SHUTDOWN),
9899
buildHidden('enable_custom_filters', props.enableCustomFilters ?? HIDE_SHUTDOWN),
99100
buildHidden('enable_web_uploads', props.enableWebUploads ?? HIDE_SHUTDOWN),
100101
BASE_HTML,

frontend/static/dtale/DataViewer.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ const selectResult = createSelector(
6060
selectors.selectIsArcticDB,
6161
selectors.selectHideMainMenu,
6262
selectors.selectHideColumnMenus,
63+
selectors.selectHideRowExpanders,
6364
],
6465
(
6566
dataId,
@@ -75,6 +76,7 @@ const selectResult = createSelector(
7576
isArcticDB,
7677
hideMainMenu,
7778
hideColumnMenus,
79+
hideRowExpanders,
7880
) => ({
7981
dataId,
8082
theme,
@@ -89,6 +91,7 @@ const selectResult = createSelector(
8991
isArcticDB,
9092
hideMainMenu,
9193
hideColumnMenus,
94+
hideRowExpanders,
9295
}),
9396
);
9497

@@ -107,6 +110,7 @@ export const DataViewer: React.FC = () => {
107110
isArcticDB,
108111
hideMainMenu,
109112
hideColumnMenus,
113+
hideRowExpanders,
110114
} = useAppSelector(selectResult);
111115
const dispatch = useAppDispatch();
112116
const closeColumnMenu = (): void => dispatch(actions.closeColumnMenu());
@@ -246,7 +250,7 @@ export const DataViewer: React.FC = () => {
246250
),
247251
}));
248252
const fullWidth = updatedColumns.reduce((res, c) => res + (c.width ?? gu.DEFAULT_COL_WIDTH), 0);
249-
if (fullWidth > window.innerWidth) {
253+
if (!hideRowExpanders && fullWidth > window.innerWidth) {
250254
updatedColumns = [
251255
updatedColumns[0],
252256
{ ...gu.EXPANDER_CFG },

frontend/static/dtale/menu/HideHeaderEditor.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ const HideHeaderEditor: React.FC<WithTranslation> = ({ t }) => {
3535
>
3636
<span className="toggler-action">
3737
<button className="btn btn-plain">
38-
<i className={`ico-check-box${hideHeaderEditor ? '' : '-outline-blank'}`} style={{ marginTop: '-.25em' }} />
38+
<i
39+
className={`ico-check-box${hideHeaderEditor ? '' : '-outline-blank'} pr-2`}
40+
style={{ marginTop: '-.25em' }}
41+
/>
3942
<span className="font-weight-bold" style={{ fontSize: '95%' }}>
4043
{t('Hide Header Editor', { ns: 'menu' })}
4144
</span>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { createSelector } from '@reduxjs/toolkit';
2+
import * as React from 'react';
3+
import { withTranslation, WithTranslation } from 'react-i18next';
4+
5+
import { AppActions } from '../../redux/actions/AppActions';
6+
import * as settingsActions from '../../redux/actions/settings';
7+
import { useAppDispatch, useAppSelector } from '../../redux/hooks';
8+
import { selectDataId, selectHideRowExpanders } from '../../redux/selectors';
9+
import * as serverState from '../serverStateManagement';
10+
11+
import { MenuItem } from './MenuItem';
12+
13+
const selectResult = createSelector([selectDataId, selectHideRowExpanders], (dataId, hideRowExpanders) => ({
14+
dataId,
15+
hideRowExpanders,
16+
}));
17+
18+
const HideRowExpanders: React.FC<WithTranslation> = ({ t }) => {
19+
const { dataId, hideRowExpanders } = useAppSelector(selectResult);
20+
const dispatch = useAppDispatch();
21+
22+
const setHideRowExpanders = async (): Promise<void> => {
23+
const updates = { hide_row_expanders: !hideRowExpanders };
24+
await serverState.updateSettings(updates, dataId);
25+
dispatch(settingsActions.updateSettings(updates));
26+
dispatch(AppActions.UpdateHideRowExpanders(!hideRowExpanders));
27+
dispatch(AppActions.HideRibbonMenuAction());
28+
};
29+
30+
return (
31+
<MenuItem
32+
className="hoverable"
33+
description={t('menu_description:hide_row_expanders')}
34+
onClick={setHideRowExpanders}
35+
>
36+
<span className="toggler-action">
37+
<button className="btn btn-plain">
38+
<i
39+
className={`ico-check-box${hideRowExpanders ? '' : '-outline-blank'} pr-2`}
40+
style={{ marginTop: '-.25em' }}
41+
/>
42+
<span className="font-weight-bold" style={{ fontSize: '95%' }}>
43+
{t('Hide Row Expanders', { ns: 'menu' })}
44+
</span>
45+
</button>
46+
</span>
47+
</MenuItem>
48+
);
49+
};
50+
51+
export default withTranslation(['menu', 'menu_description'])(HideRowExpanders);

frontend/static/dtale/menu/ShowNonNumericHeatmapColumns.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ const ShowNonNumericHeatmapColumns: React.FC<WithTranslation> = ({ t }) => {
4747
<span className="toggler-action">
4848
<button className="btn btn-plain">
4949
<i
50-
className={`ico-check-box${showAllHeatmapColumns ? '' : '-outline-blank'}`}
50+
className={`ico-check-box${showAllHeatmapColumns ? '' : '-outline-blank'} pr-2`}
5151
style={{ marginTop: '-.25em' }}
5252
/>
5353
<span className="font-weight-bold" style={{ fontSize: '95%' }}>

frontend/static/dtale/menu/VerticalColumnHeaders.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ const VerticalColumnHeaders: React.FC<WithTranslation> = ({ t }) => {
3030
<MenuItem className="hoverable" description={t('menu_description:vertical_headers')} onClick={setVerticalHeaders}>
3131
<span className="toggler-action">
3232
<button className="btn btn-plain">
33-
<i className={`ico-check-box${verticalHeaders ? '' : '-outline-blank'}`} style={{ marginTop: '-.25em' }} />
33+
<i
34+
className={`ico-check-box${verticalHeaders ? '' : '-outline-blank'} pr-2`}
35+
style={{ marginTop: '-.25em' }}
36+
/>
3437
<span className="font-weight-bold" style={{ fontSize: '95%' }}>
3538
{t('Vertical Column Headers', { ns: 'menu' })}
3639
</span>

frontend/static/dtale/ribbon/RibbonDropdown.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import FilterOption from '../menu/FilterOption';
2727
import GageRnROption from '../menu/GageRnROption';
2828
import HeatMapOption from '../menu/HeatMapOption';
2929
import HideHeaderEditor from '../menu/HideHeaderEditor';
30+
import HideRowExpanders from '../menu/HideRowExpanders';
3031
import HighlightOption from '../menu/HighlightOption';
3132
import InstancesOption from '../menu/InstancesOption';
3233
import JumpToColumnOption from '../menu/JumpToColumnOption';
@@ -177,7 +178,7 @@ const RibbonDropdown: React.FC<RibbonDropdownProps & WithTranslation> = ({ colum
177178
<div
178179
className={`ribbon-menu-dd-content${visible ? ' is-expanded' : ''}`}
179180
data-testid="ribbon-dropdown"
180-
style={style}
181+
style={{ ...style, ...(name === RibbonDropdownType.SETTINGS ? { minWidth: '16em' } : {}) }}
181182
ref={ref}
182183
onClick={(e): void => {
183184
e.stopPropagation();
@@ -306,6 +307,7 @@ const RibbonDropdown: React.FC<RibbonDropdownProps & WithTranslation> = ({ colum
306307
<ShowNonNumericHeatmapColumns />
307308
<VerticalColumnHeaders />
308309
<HideHeaderEditor />
310+
<HideRowExpanders />
309311
</ul>
310312
)}
311313
</div>

0 commit comments

Comments
 (0)