From 68fb6f3f3bcadb460492b6a6ec76dada511eb4ad Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Tue, 20 Jun 2017 23:52:42 -0700 Subject: [PATCH] SONAR-9253 Remember last selected options on projects page (#2184) --- .../it/projectSearch/ProjectsPageTest.java | 2 + .../apps/projects/components/AllProjects.js | 56 +++++++++++++++---- .../src/main/js/apps/projects/utils.js | 39 +++++++++---- 3 files changed, 74 insertions(+), 23 deletions(-) diff --git a/it/it-tests/src/test/java/it/projectSearch/ProjectsPageTest.java b/it/it-tests/src/test/java/it/projectSearch/ProjectsPageTest.java index a3c9b0f9793..f33f5b14b6a 100644 --- a/it/it-tests/src/test/java/it/projectSearch/ProjectsPageTest.java +++ b/it/it-tests/src/test/java/it/projectSearch/ProjectsPageTest.java @@ -33,6 +33,7 @@ import pageobjects.Navigation; import pageobjects.projects.ProjectsPage; import util.user.UserRule; +import static com.codeborne.selenide.Selenide.clearBrowserLocalStorage; import static com.codeborne.selenide.WebDriverRunner.url; import static org.assertj.core.api.Assertions.assertThat; import static util.ItUtils.newUserWsClient; @@ -64,6 +65,7 @@ public class ProjectsPageTest { public void before() { adminUser = userRule.createAdminUser(); userAdminWsClient = newUserWsClient(ORCHESTRATOR, adminUser, adminUser); + clearBrowserLocalStorage(); } @Test diff --git a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js index 93754d09541..746a6fee39e 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js +++ b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js @@ -27,7 +27,7 @@ import PageSidebar from './PageSidebar'; import VisualizationsContainer from '../visualizations/VisualizationsContainer'; import { parseUrlQuery } from '../store/utils'; import { translate } from '../../../helpers/l10n'; -import { SORTING_SWITCH, parseSorting } from '../utils'; +import * as utils from '../utils'; import '../styles.css'; type Props = {| @@ -35,7 +35,10 @@ type Props = {| location: { pathname: string, query: { [string]: string } }, fetchProjects: (query: string, isFavorite: boolean, organization?: {}) => Promise<*>, organization?: { key: string }, - router: { push: ({ pathname: string, query?: {} }) => void }, + router: { + push: ({ pathname: string, query?: {} }) => void, + replace: ({ pathname: string, query?: {} }) => void + }, currentUser?: { isLoggedIn: boolean } |}; @@ -48,14 +51,14 @@ export default class AllProjects extends React.PureComponent { state: State = { query: {} }; componentDidMount() { - this.handleQueryChange(); + this.handleQueryChange(true); const footer = document.getElementById('footer'); footer && footer.classList.add('search-navigator-footer'); } componentDidUpdate(prevProps: Props) { if (prevProps.location.query !== this.props.location.query) { - this.handleQueryChange(); + this.handleQueryChange(false); } } @@ -72,6 +75,20 @@ export default class AllProjects extends React.PureComponent { isFiltered = () => Object.keys(this.state.query).some(key => this.state.query[key] != null); + getSavedOptions = () => { + const options = {}; + if (utils.getSort()) { + options.sort = utils.getSort(); + } + if (utils.getView()) { + options.view = utils.getView(); + } + if (utils.getVisualization()) { + options.visualization = utils.getVisualization(); + } + return options; + }; + handlePerspectiveChange = ({ view, visualization }: { view: string, visualization?: string }) => { const query: { view: ?string, visualization: ?string, sort?: ?string } = { view: view === 'overall' ? undefined : view, @@ -80,24 +97,39 @@ export default class AllProjects extends React.PureComponent { if (this.state.query.view === 'leak' || view === 'leak') { if (this.state.query.sort) { - const sort = parseSorting(this.state.query.sort); - if (SORTING_SWITCH[sort.sortValue]) { - query.sort = (sort.sortDesc ? '-' : '') + SORTING_SWITCH[sort.sortValue]; + const sort = utils.parseSorting(this.state.query.sort); + if (utils.SORTING_SWITCH[sort.sortValue]) { + query.sort = (sort.sortDesc ? '-' : '') + utils.SORTING_SWITCH[sort.sortValue]; } } this.props.router.push({ pathname: this.props.location.pathname, query }); } else { this.updateLocationQuery(query); } + + utils.saveSort(query.sort); + utils.saveView(query.view); + utils.saveVisualization(visualization); }; - handleSortChange = (sort: string, desc: boolean) => - this.updateLocationQuery({ sort: (desc ? '-' : '') + sort }); + handleSortChange = (sort: string, desc: boolean) => { + const asString = (desc ? '-' : '') + sort; + this.updateLocationQuery({ sort: asString }); + utils.saveSort(asString); + }; - handleQueryChange() { + handleQueryChange(initialMount: boolean) { const query = parseUrlQuery(this.props.location.query); - this.setState({ query }); - this.props.fetchProjects(query, this.props.isFavorite, this.props.organization); + const savedOptions = this.getSavedOptions(); + const savedOptionsSet = savedOptions.sort || savedOptions.view || savedOptions.visualization; + + // if there is no filter, but there are saved preferences in the localStorage + if (initialMount && !this.isFiltered() && savedOptionsSet) { + this.props.router.replace({ pathname: this.props.location.pathname, query: savedOptions }); + } else { + this.setState({ query }); + this.props.fetchProjects(query, this.props.isFavorite, this.props.organization); + } } updateLocationQuery = (newQuery: { [string]: ?string }) => { diff --git a/server/sonar-web/src/main/js/apps/projects/utils.js b/server/sonar-web/src/main/js/apps/projects/utils.js index 008bdf5cb6a..68b26f1b41c 100644 --- a/server/sonar-web/src/main/js/apps/projects/utils.js +++ b/server/sonar-web/src/main/js/apps/projects/utils.js @@ -20,32 +20,49 @@ // @flow import { translate } from '../../helpers/l10n'; -const LOCALSTORAGE_KEY = 'sonarqube.projects.default'; -const LOCALSTORAGE_FAVORITE = 'favorite'; -const LOCALSTORAGE_ALL = 'all'; +const DEFAULT_FILTER = 'sonarqube.projects.default'; +const FAVORITE = 'favorite'; +const ALL = 'all'; + +const VIEW = 'sonarqube.projects.view'; +const VISUALIZATION = 'sonarqube.projects.visualization'; +const SORT = 'sonarqube.projects.sort'; export const isFavoriteSet = (): boolean => { - const setting = window.localStorage.getItem(LOCALSTORAGE_KEY); - return setting === LOCALSTORAGE_FAVORITE; + const setting = window.localStorage.getItem(DEFAULT_FILTER); + return setting === FAVORITE; }; export const isAllSet = (): boolean => { - const setting = window.localStorage.getItem(LOCALSTORAGE_KEY); - return setting === LOCALSTORAGE_ALL; + const setting = window.localStorage.getItem(DEFAULT_FILTER); + return setting === ALL; }; -const save = (value: string) => { +const save = (key: string, value: ?string) => { try { - window.localStorage.setItem(LOCALSTORAGE_KEY, value); + if (value) { + window.localStorage.setItem(key, value); + } else { + window.localStorage.removeItem(key); + } } catch (e) { // usually that means the storage is full // just do nothing in this case } }; -export const saveAll = () => save(LOCALSTORAGE_ALL); +export const saveAll = () => save(DEFAULT_FILTER, ALL); + +export const saveFavorite = () => save(DEFAULT_FILTER, FAVORITE); + +export const saveView = (view: ?string) => save(VIEW, view); +export const getView = () => window.localStorage.getItem(VIEW); + +export const saveVisualization = (visualization: ?string) => save(VISUALIZATION, visualization); +export const getVisualization = () => window.localStorage.getItem(VISUALIZATION); -export const saveFavorite = () => save(LOCALSTORAGE_FAVORITE); +export const saveSort = (sort: ?string) => save(SORT, sort); +export const getSort = () => window.localStorage.getItem(SORT); export const SORTING_METRICS = [ { value: 'name' }, -- 2.39.5