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

Add disable preview toggle in settings panel #1977

Merged
merged 38 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
fe8c0de
add disable preview toggle
SajidAlamQB Jul 10, 2024
5a83933
Merge branch 'main' into feat/disable-preview
SajidAlamQB Jul 10, 2024
527529c
Merge branch 'main' into feat/disable-preview
SajidAlamQB Jul 10, 2024
7e69aca
rename and add api call
SajidAlamQB Jul 10, 2024
cddf0be
add to localstore
SajidAlamQB Jul 11, 2024
5c7664f
api backend implementation
SajidAlamQB Jul 15, 2024
9204db6
add new api endpoint
SajidAlamQB Jul 16, 2024
7731c0d
changes based on review
SajidAlamQB Jul 17, 2024
1f22e33
coverage
SajidAlamQB Jul 17, 2024
41137d9
add tests
SajidAlamQB Jul 17, 2024
c4cc6a1
lint
SajidAlamQB Jul 17, 2024
90ca5d0
design changes
SajidAlamQB Jul 18, 2024
58538bd
Merge branch 'main' into feat/disable-preview
SajidAlamQB Jul 18, 2024
b9d07f0
changes based on review 2
SajidAlamQB Jul 18, 2024
7468b5d
changes based on review 3
SajidAlamQB Jul 19, 2024
f40d627
add reducer test
SajidAlamQB Jul 19, 2024
1b90d05
elint fix
SajidAlamQB Jul 19, 2024
46eb7ef
changes based on review
SajidAlamQB Jul 22, 2024
c38c51b
Update test_router.py
SajidAlamQB Jul 22, 2024
99aade6
Update router.py
SajidAlamQB Jul 22, 2024
9ec067d
remove fstring
SajidAlamQB Jul 22, 2024
3657cfa
Add new endpoint to fetch initial showDatasetsPreviews value
SajidAlamQB Jul 22, 2024
013ca1c
coverage
SajidAlamQB Jul 22, 2024
82bc31c
Merge branch 'main' into feat/disable-preview
SajidAlamQB Jul 22, 2024
1d1796e
redux thunk for preferences API
SajidAlamQB Jul 23, 2024
0efaee1
Update preferences.js
SajidAlamQB Jul 23, 2024
03b4a50
Update settings-modal.js
SajidAlamQB Jul 23, 2024
055ed76
revert nested
SajidAlamQB Jul 23, 2024
a6603bf
changes based on review
SajidAlamQB Jul 23, 2024
f184349
combine preference action
SajidAlamQB Jul 24, 2024
7a070e0
changes based on reviews
SajidAlamQB Jul 24, 2024
4f048c2
Update settings-modal.test.js
SajidAlamQB Jul 24, 2024
19013bd
Merge branch 'main' into feat/disable-preview
SajidAlamQB Jul 24, 2024
a9a7419
Update RELEASE.md
SajidAlamQB Jul 24, 2024
07bb7b7
rename and add nesting
SajidAlamQB Jul 24, 2024
2c80e6a
update tests
SajidAlamQB Jul 24, 2024
f8aeeca
Merge branch 'main' into feat/disable-preview
SajidAlamQB Jul 24, 2024
b26b754
changes based on reviews
SajidAlamQB Jul 25, 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
1 change: 1 addition & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Please follow the established format:
- Refactor namespace pipelines. (#1897)
- Expose the internal Redux state through `options` prop while using Kedro-Viz as a React component. (#1969)
- Enhance documentation for the Kedro-Viz standalone React component. (#1954)
- Add Datasets preview toggle in the settings panel. (#1977)

## Bug fixes and other changes

Expand Down
6 changes: 6 additions & 0 deletions package/kedro_viz/api/rest/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,9 @@ class DeployerConfiguration(BaseModel):
is_all_previews_enabled: bool = False
endpoint: str
bucket_name: str


class UserPreference(BaseModel):
"""User preferences for Kedro Viz."""

showDatasetPreviews: bool
35 changes: 33 additions & 2 deletions package/kedro_viz/api/rest/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
from fastapi import APIRouter
from fastapi.responses import JSONResponse

from kedro_viz.api.rest.requests import DeployerConfiguration
from kedro_viz.api.rest.requests import DeployerConfiguration, UserPreference
from kedro_viz.constants import PACKAGE_REQUIREMENTS
from kedro_viz.integrations.deployment.deployer_factory import DeployerFactory

from .responses import (
APIErrorMessage,
DataNodeMetadata,
GraphAPIResponse,
NodeMetadataAPIResponse,
PackageCompatibilityAPIResponse,
Expand Down Expand Up @@ -49,6 +50,36 @@ async def get_single_node_metadata(node_id: str):
return get_node_metadata_response(node_id)


@router.post("/preferences")
SajidAlamQB marked this conversation as resolved.
Show resolved Hide resolved
async def update_preferences(preferences: UserPreference):
try:
DataNodeMetadata.set_is_all_previews_enabled(preferences.showDatasetPreviews)
return JSONResponse(
status_code=200, content={"message": "Preferences updated successfully"}
)
except Exception as exception:
logger.error("Failed to update preferences: %s", str(exception))
return JSONResponse(
status_code=500,
content={"message": "Failed to update preferences"},
)


@router.get("/preferences", response_model=UserPreference)
async def get_preferences():
try:
show_dataset_previews = DataNodeMetadata.is_all_previews_enabled
return JSONResponse(
status_code=200, content={"showDatasetPreviews": show_dataset_previews}
)
except Exception as exception:
logger.error("Failed to fetch preferences: %s", str(exception))
return JSONResponse(
status_code=500,
content={"message": "Failed to fetch preferences"},
)


@router.get(
"/pipelines/{registered_pipeline_id}",
response_model=GraphAPIResponse,
Expand Down Expand Up @@ -99,7 +130,7 @@ async def get_package_compatibilities():
return get_package_compatibilities_response(PACKAGE_REQUIREMENTS)
except Exception as exc:
logger.exception(
"An exception occured while getting package compatibility info : %s", exc
"An exception occurred while getting package compatibility info : %s", exc
)
return JSONResponse(
status_code=500,
Expand Down
42 changes: 42 additions & 0 deletions package/tests/test_api/test_rest/test_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,45 @@ def test_get_package_compatibilities(

assert response.status_code == expected_status_code
assert response.json() == expected_response


def test_update_preferences_success(client, mocker):
mocker.patch(
"kedro_viz.api.rest.responses.DataNodeMetadata.set_is_all_previews_enabled"
)
response = client.post("api/preferences", json={"showDatasetPreviews": True})

assert response.status_code == 200
assert response.json() == {"message": "Preferences updated successfully"}


def test_update_preferences_failure(client, mocker):
mocker.patch(
"kedro_viz.api.rest.responses.DataNodeMetadata.set_is_all_previews_enabled",
side_effect=Exception("Test Exception"),
)
response = client.post("api/preferences", json={"showDatasetPreviews": True})

assert response.status_code == 500
assert response.json() == {"message": "Failed to update preferences"}


def test_get_preferences_success(client, mocker):
mocker.patch(
"kedro_viz.api.rest.responses.DataNodeMetadata", is_all_previews_enabled=True
)
response = client.get("/api/preferences")

assert response.status_code == 200
assert response.json() == {"showDatasetPreviews": True}


def test_get_preferences_failure(client, mocker):
mocker.patch(
"kedro_viz.api.rest.responses.DataNodeMetadata.is_all_previews_enabled",
side_effect=Exception("Test Exception"),
)
response = client.get("/api/preferences")

assert response.status_code == 500
assert response.json() == {"message": "Failed to fetch preferences"}
19 changes: 19 additions & 0 deletions src/actions/preferences.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { fetchPreferences } from '../utils/preferences-api';

// Action Types
export const UPDATE_USER_PREFERENCES = 'UPDATE_USER_PREFERENCES';

// Action Creators
export const updateUserPreferences = (preferences) => ({
type: UPDATE_USER_PREFERENCES,
payload: preferences,
});

export const getPreferences = () => async (dispatch) => {
try {
const preferences = await fetchPreferences();
dispatch(updateUserPreferences(preferences));
} catch (error) {
console.error('Error fetching preferences:', error);
}
};
55 changes: 53 additions & 2 deletions src/components/settings-modal/settings-modal.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useCallback } from 'react';
import { connect } from 'react-redux';
import {
changeFlag,
toggleShowFeatureHints,
toggleIsPrettyName,
toggleSettingsModal,
} from '../../actions';
import {
getPreferences,
updateUserPreferences,
} from '../../actions/preferences';
import { getFlagsState } from '../../utils/flags';
import SettingsModalRow from './settings-modal-row';
import { settings as settingsConfig, localStorageName } from '../../config';
import { saveLocalStorage } from '../../store/helpers';
import { localStorageKeyFeatureHintsStep } from '../../components/feature-hints/feature-hints';
import { updatePreferences } from '../../utils/preferences-api';

import Button from '../ui/button';
import Modal from '../ui/modal';
Expand All @@ -27,11 +32,14 @@ const SettingsModal = ({
showFeatureHints,
isOutdated,
isPrettyName,
showDatasetPreviews,
latestVersion,
onToggleFlag,
onToggleShowFeatureHints,
onToggleIsPrettyName,
onToggleShowDatasetPreviews,
showSettingsModal,
getPreferences,
visible,
}) => {
const flagData = getFlagsState();
Expand All @@ -40,12 +48,32 @@ const SettingsModal = ({
const [isPrettyNameValue, setIsPrettyName] = useState(isPrettyName);
const [showFeatureHintsValue, setShowFeatureHintsValue] =
useState(showFeatureHints);
const [showDatasetPreviewsValue, setShowDatasetPreviewsValue] =
useState(showDatasetPreviews);
const [toggleFlags, setToggleFlags] = useState(flags);

useEffect(() => {
setShowFeatureHintsValue(showFeatureHints);
}, [showFeatureHints]);

useEffect(() => {
setShowDatasetPreviewsValue(showDatasetPreviews);
}, [showDatasetPreviews]);

useEffect(() => {
if (visible.settingsModal) {
getPreferences();
ravi-kumar-pilla marked this conversation as resolved.
Show resolved Hide resolved
}
}, [visible.settingsModal, getPreferences]);

const handleSavePreferences = useCallback(async () => {
try {
await updatePreferences(showDatasetPreviewsValue);
} catch (error) {
console.error('Error updating preferences:', error);
}
}, [showDatasetPreviewsValue]);

useEffect(() => {
let modalTimeout, resetTimeout;

Expand All @@ -63,8 +91,10 @@ const SettingsModal = ({
return onToggleFlag(name, value);
});

handleSavePreferences();
onToggleIsPrettyName(isPrettyNameValue);
onToggleShowFeatureHints(showFeatureHintsValue);
onToggleShowDatasetPreviews(showDatasetPreviewsValue);
setHasNotInteracted(true);
SajidAlamQB marked this conversation as resolved.
Show resolved Hide resolved
setHasClickApplyAndClose(false);

Expand All @@ -80,19 +110,23 @@ const SettingsModal = ({
hasClickedApplyAndClose,
showFeatureHintsValue,
isPrettyNameValue,
showDatasetPreviewsValue,
onToggleFlag,
onToggleShowFeatureHints,
onToggleIsPrettyName,
onToggleShowDatasetPreviews,
showSettingsModal,
toggleFlags,
handleSavePreferences,
]);

const resetStateCloseModal = () => {
showSettingsModal(false);
setHasNotInteracted(true);
setToggleFlags(flags);
setIsPrettyName(isPrettyName);
setShowFeatureHintsValue(showFeatureHintsValue);
setShowFeatureHintsValue(showFeatureHints);
setShowDatasetPreviewsValue(showDatasetPreviews);
};

return (
Expand Down Expand Up @@ -130,6 +164,16 @@ const SettingsModal = ({
}
}}
/>
<SettingsModalRow
id="showDatasetPreviews"
name={settingsConfig['showDatasetPreviews'].name}
toggleValue={showDatasetPreviewsValue}
description={settingsConfig['showDatasetPreviews'].description}
onToggleChange={(event) => {
setShowDatasetPreviewsValue(event.target.checked);
setHasNotInteracted(false);
}}
/>
{flagData.map(({ name, value, description }) => (
<SettingsModalRow
description={description}
Expand Down Expand Up @@ -209,13 +253,17 @@ export const mapStateToProps = (state) => ({
flags: state.flags,
showFeatureHints: state.showFeatureHints,
isPrettyName: state.isPrettyName,
showDatasetPreviews: state.userPreferences.showDatasetPreviews,
visible: state.visible,
});

export const mapDispatchToProps = (dispatch) => ({
showSettingsModal: (value) => {
dispatch(toggleSettingsModal(value));
},
getPreferences: () => {
dispatch(getPreferences());
},
onToggleFlag: (name, value) => {
dispatch(changeFlag(name, value));
},
Expand All @@ -225,6 +273,9 @@ export const mapDispatchToProps = (dispatch) => ({
onToggleShowFeatureHints: (value) => {
dispatch(toggleShowFeatureHints(value));
},
onToggleShowDatasetPreviews: (value) => {
dispatch(updateUserPreferences({ showDatasetPreviews: value }));
},
});

export default connect(mapStateToProps, mapDispatchToProps)(SettingsModal);
7 changes: 7 additions & 0 deletions src/components/settings-modal/settings-modal.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ describe('SettingsModal', () => {
exportModal: expect.any(Boolean),
settingsModal: expect.any(Boolean),
}),
showDatasetPreviews: expect.any(Boolean),
flags: expect.any(Object),
isPrettyName: expect.any(Boolean),
showFeatureHints: expect.any(Boolean),
Expand Down Expand Up @@ -73,5 +74,11 @@ describe('SettingsModal', () => {
type: 'TOGGLE_IS_PRETTY_NAME',
isPrettyName: false,
});

mapDispatchToProps(dispatch).onToggleShowDatasetPreviews(false);
expect(dispatch.mock.calls[3][0]).toEqual({
type: 'UPDATE_USER_PREFERENCES',
payload: { showDatasetPreviews: false },
});
});
});
5 changes: 5 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ export const settings = {
description: 'Enable or disable all new feature hints in the interface.',
default: true,
},
showDatasetPreviews: {
name: 'Dataset previews',
description: 'Display preview data for all datasets.',
default: true,
},
};

// Sidebar groups is an ordered map of { id: label }
Expand Down
2 changes: 2 additions & 0 deletions src/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
TOGGLE_EXPAND_ALL_PIPELINES,
UPDATE_STATE_FROM_OPTIONS,
} from '../actions';
import userPreferences from './preferences';
import { TOGGLE_PARAMETERS_HOVERED } from '../actions';

/**
Expand Down Expand Up @@ -81,6 +82,7 @@ const combinedReducer = combineReducers({
modularPipeline,
visible,
runsMetadata,
userPreferences,
// These props don't have any actions associated with them
display: createReducer(null),
dataSource: createReducer(null),
Expand Down
19 changes: 19 additions & 0 deletions src/reducers/preferences.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { UPDATE_USER_PREFERENCES } from '../actions/preferences';

const initialState = {
showDatasetPreviews: true,
};

const userPreferences = (state = initialState, action) => {
switch (action.type) {
case UPDATE_USER_PREFERENCES:
return {
...state,
showDatasetPreviews: action.payload.showDatasetPreviews,
};
default:
return state;
}
};

export default userPreferences;
14 changes: 14 additions & 0 deletions src/reducers/reducers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
import { UPDATE_ACTIVE_PIPELINE } from '../actions/pipelines';
import { TOGGLE_MODULAR_PIPELINE_ACTIVE } from '../actions/modular-pipelines';
import { TOGGLE_GRAPH_LOADING } from '../actions/graph';
import { UPDATE_USER_PREFERENCES } from '../actions/preferences';

describe('Reducer', () => {
it('should return an Object', () => {
Expand Down Expand Up @@ -141,6 +142,19 @@ describe('Reducer', () => {
});
});

describe('UPDATE_USER_PREFERENCES', () => {
it('should update the value of showDatasetPreviews', () => {
const newState = reducer(mockState.spaceflights, {
type: UPDATE_USER_PREFERENCES,
payload: { showDatasetPreviews: true },
});
expect(mockState.spaceflights.userPreferences.showDatasetPreviews).toBe(
true
);
expect(newState.userPreferences.showDatasetPreviews).toBe(true);
});
});

describe('TOGGLE_TEXT_LABELS', () => {
it('should toggle the value of textLabels', () => {
const newState = reducer(mockState.spaceflights, {
Expand Down
Loading
Loading