fix: persistent view style (#4138)
This commit is contained in:
parent
5baa20bf67
commit
017883f0dc
packages/netlify-cms-core/src
actions
components/Collection
reducers
types
@ -73,6 +73,8 @@ export const ENTRY_DELETE_FAILURE = 'ENTRY_DELETE_FAILURE';
|
|||||||
export const ADD_DRAFT_ENTRY_MEDIA_FILE = 'ADD_DRAFT_ENTRY_MEDIA_FILE';
|
export const ADD_DRAFT_ENTRY_MEDIA_FILE = 'ADD_DRAFT_ENTRY_MEDIA_FILE';
|
||||||
export const REMOVE_DRAFT_ENTRY_MEDIA_FILE = 'REMOVE_DRAFT_ENTRY_MEDIA_FILE';
|
export const REMOVE_DRAFT_ENTRY_MEDIA_FILE = 'REMOVE_DRAFT_ENTRY_MEDIA_FILE';
|
||||||
|
|
||||||
|
export const CHANGE_VIEW_STYLE = 'CHANGE_VIEW_STYLE';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Simple Action Creators (Internal)
|
* Simple Action Creators (Internal)
|
||||||
* We still need to export them for tests
|
* We still need to export them for tests
|
||||||
@ -240,6 +242,15 @@ export function filterByField(collection: Collection, filter: ViewFilter) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function changeViewStyle(viewStyle: string) {
|
||||||
|
return {
|
||||||
|
type: CHANGE_VIEW_STYLE,
|
||||||
|
payload: {
|
||||||
|
style: viewStyle,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function entryPersisting(collection: Collection, entry: EntryMap) {
|
export function entryPersisting(collection: Collection, entry: EntryMap) {
|
||||||
return {
|
return {
|
||||||
type: ENTRY_PERSIST_REQUEST,
|
type: ENTRY_PERSIST_REQUEST,
|
||||||
|
@ -11,10 +11,9 @@ import CollectionTop from './CollectionTop';
|
|||||||
import EntriesCollection from './Entries/EntriesCollection';
|
import EntriesCollection from './Entries/EntriesCollection';
|
||||||
import EntriesSearch from './Entries/EntriesSearch';
|
import EntriesSearch from './Entries/EntriesSearch';
|
||||||
import CollectionControls from './CollectionControls';
|
import CollectionControls from './CollectionControls';
|
||||||
import { sortByField, filterByField } from '../../actions/entries';
|
import { sortByField, filterByField, changeViewStyle } from '../../actions/entries';
|
||||||
import { selectSortableFields, selectViewFilters } from '../../reducers/collections';
|
import { selectSortableFields, selectViewFilters } from '../../reducers/collections';
|
||||||
import { selectEntriesSort, selectEntriesFilter } from '../../reducers/entries';
|
import { selectEntriesSort, selectEntriesFilter, selectViewStyle } from '../../reducers/entries';
|
||||||
import { VIEW_STYLE_LIST } from '../../constants/collectionViews';
|
|
||||||
|
|
||||||
const CollectionContainer = styled.div`
|
const CollectionContainer = styled.div`
|
||||||
margin: ${lengths.pageMargin};
|
margin: ${lengths.pageMargin};
|
||||||
@ -46,18 +45,10 @@ export class Collection extends React.Component {
|
|||||||
onSortClick: PropTypes.func.isRequired,
|
onSortClick: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
|
||||||
viewStyle: VIEW_STYLE_LIST,
|
|
||||||
};
|
|
||||||
|
|
||||||
renderEntriesCollection = () => {
|
renderEntriesCollection = () => {
|
||||||
const { collection, filterTerm } = this.props;
|
const { collection, filterTerm, viewStyle } = this.props;
|
||||||
return (
|
return (
|
||||||
<EntriesCollection
|
<EntriesCollection collection={collection} viewStyle={viewStyle} filterTerm={filterTerm} />
|
||||||
collection={collection}
|
|
||||||
viewStyle={this.state.viewStyle}
|
|
||||||
filterTerm={filterTerm}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -71,12 +62,6 @@ export class Collection extends React.Component {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleChangeViewStyle = viewStyle => {
|
|
||||||
if (this.state.viewStyle !== viewStyle) {
|
|
||||||
this.setState({ viewStyle });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
collection,
|
collection,
|
||||||
@ -93,6 +78,8 @@ export class Collection extends React.Component {
|
|||||||
t,
|
t,
|
||||||
onFilterClick,
|
onFilterClick,
|
||||||
filter,
|
filter,
|
||||||
|
onChangeViewStyle,
|
||||||
|
viewStyle,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
let newEntryUrl = collection.get('create') ? getNewEntryUrl(collectionName) : '';
|
let newEntryUrl = collection.get('create') ? getNewEntryUrl(collectionName) : '';
|
||||||
@ -125,8 +112,8 @@ export class Collection extends React.Component {
|
|||||||
<>
|
<>
|
||||||
<CollectionTop collection={collection} newEntryUrl={newEntryUrl} />
|
<CollectionTop collection={collection} newEntryUrl={newEntryUrl} />
|
||||||
<CollectionControls
|
<CollectionControls
|
||||||
viewStyle={this.state.viewStyle}
|
viewStyle={viewStyle}
|
||||||
onChangeViewStyle={this.handleChangeViewStyle}
|
onChangeViewStyle={onChangeViewStyle}
|
||||||
sortableFields={sortableFields}
|
sortableFields={sortableFields}
|
||||||
onSortClick={onSortClick}
|
onSortClick={onSortClick}
|
||||||
sort={sort}
|
sort={sort}
|
||||||
@ -153,6 +140,7 @@ function mapStateToProps(state, ownProps) {
|
|||||||
const sortableFields = selectSortableFields(collection, t);
|
const sortableFields = selectSortableFields(collection, t);
|
||||||
const viewFilters = selectViewFilters(collection);
|
const viewFilters = selectViewFilters(collection);
|
||||||
const filter = selectEntriesFilter(state.entries, collection.get('name'));
|
const filter = selectEntriesFilter(state.entries, collection.get('name'));
|
||||||
|
const viewStyle = selectViewStyle(state.entries);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
collection,
|
collection,
|
||||||
@ -165,12 +153,14 @@ function mapStateToProps(state, ownProps) {
|
|||||||
sortableFields,
|
sortableFields,
|
||||||
viewFilters,
|
viewFilters,
|
||||||
filter,
|
filter,
|
||||||
|
viewStyle,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
sortByField,
|
sortByField,
|
||||||
filterByField,
|
filterByField,
|
||||||
|
changeViewStyle,
|
||||||
};
|
};
|
||||||
|
|
||||||
const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
||||||
@ -180,6 +170,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
|||||||
onSortClick: (key, direction) =>
|
onSortClick: (key, direction) =>
|
||||||
dispatchProps.sortByField(stateProps.collection, key, direction),
|
dispatchProps.sortByField(stateProps.collection, key, direction),
|
||||||
onFilterClick: filter => dispatchProps.filterByField(stateProps.collection, filter),
|
onFilterClick: filter => dispatchProps.filterByField(stateProps.collection, filter),
|
||||||
|
onChangeViewStyle: viewStyle => dispatchProps.changeViewStyle(viewStyle),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
17
packages/netlify-cms-core/src/components/Collection/__tests__/__snapshots__/Collection.spec.js.snap
17
packages/netlify-cms-core/src/components/Collection/__tests__/__snapshots__/Collection.spec.js.snap
@ -30,12 +30,10 @@ exports[`Collection should render connected component 1`] = `
|
|||||||
filter="Map {}"
|
filter="Map {}"
|
||||||
sortablefields=""
|
sortablefields=""
|
||||||
viewfilters=""
|
viewfilters=""
|
||||||
viewstyle="VIEW_STYLE_LIST"
|
|
||||||
/>
|
/>
|
||||||
<mock-entries-collection
|
<mock-entries-collection
|
||||||
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [] }"
|
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [] }"
|
||||||
filterterm=""
|
filterterm=""
|
||||||
viewstyle="VIEW_STYLE_LIST"
|
|
||||||
/>
|
/>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
@ -66,12 +64,9 @@ exports[`Collection should render with collection with create url 1`] = `
|
|||||||
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": true }"
|
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": true }"
|
||||||
newentryurl="/collections/pages/new"
|
newentryurl="/collections/pages/new"
|
||||||
/>
|
/>
|
||||||
<mock-collection-controls
|
<mock-collection-controls />
|
||||||
viewstyle="VIEW_STYLE_LIST"
|
|
||||||
/>
|
|
||||||
<mock-entries-collection
|
<mock-entries-collection
|
||||||
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": true }"
|
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": true }"
|
||||||
viewstyle="VIEW_STYLE_LIST"
|
|
||||||
/>
|
/>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
@ -103,13 +98,10 @@ exports[`Collection should render with collection with create url and path 1`] =
|
|||||||
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": true }"
|
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": true }"
|
||||||
newentryurl="/collections/pages/new?path=dir1/dir2"
|
newentryurl="/collections/pages/new?path=dir1/dir2"
|
||||||
/>
|
/>
|
||||||
<mock-collection-controls
|
<mock-collection-controls />
|
||||||
viewstyle="VIEW_STYLE_LIST"
|
|
||||||
/>
|
|
||||||
<mock-entries-collection
|
<mock-entries-collection
|
||||||
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": true }"
|
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": true }"
|
||||||
filterterm="dir1/dir2"
|
filterterm="dir1/dir2"
|
||||||
viewstyle="VIEW_STYLE_LIST"
|
|
||||||
/>
|
/>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
@ -140,12 +132,9 @@ exports[`Collection should render with collection without create url 1`] = `
|
|||||||
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": false }"
|
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": false }"
|
||||||
newentryurl=""
|
newentryurl=""
|
||||||
/>
|
/>
|
||||||
<mock-collection-controls
|
<mock-collection-controls />
|
||||||
viewstyle="VIEW_STYLE_LIST"
|
|
||||||
/>
|
|
||||||
<mock-entries-collection
|
<mock-entries-collection
|
||||||
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": false }"
|
collection="Map { \\"name\\": \\"pages\\", \\"sortableFields\\": List [], \\"view_filters\\": List [], \\"create\\": false }"
|
||||||
viewstyle="VIEW_STYLE_LIST"
|
|
||||||
/>
|
/>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,6 +14,7 @@ import {
|
|||||||
FILTER_ENTRIES_REQUEST,
|
FILTER_ENTRIES_REQUEST,
|
||||||
FILTER_ENTRIES_SUCCESS,
|
FILTER_ENTRIES_SUCCESS,
|
||||||
FILTER_ENTRIES_FAILURE,
|
FILTER_ENTRIES_FAILURE,
|
||||||
|
CHANGE_VIEW_STYLE,
|
||||||
} from '../actions/entries';
|
} from '../actions/entries';
|
||||||
import { SEARCH_ENTRIES_SUCCESS } from '../actions/search';
|
import { SEARCH_ENTRIES_SUCCESS } from '../actions/search';
|
||||||
import {
|
import {
|
||||||
@ -42,12 +43,14 @@ import {
|
|||||||
FilterMap,
|
FilterMap,
|
||||||
EntriesFilterRequestPayload,
|
EntriesFilterRequestPayload,
|
||||||
EntriesFilterFailurePayload,
|
EntriesFilterFailurePayload,
|
||||||
|
ChangeViewStylePayload,
|
||||||
} from '../types/redux';
|
} from '../types/redux';
|
||||||
import { folderFormatter } from '../lib/formatters';
|
import { folderFormatter } from '../lib/formatters';
|
||||||
import { isAbsolutePath, basename } from 'netlify-cms-lib-util';
|
import { isAbsolutePath, basename } from 'netlify-cms-lib-util';
|
||||||
import { trim, once, sortBy, set, orderBy } from 'lodash';
|
import { trim, once, sortBy, set, orderBy } from 'lodash';
|
||||||
import { selectSortDataPath } from './collections';
|
import { selectSortDataPath } from './collections';
|
||||||
import { stringTemplate } from 'netlify-cms-lib-widgets';
|
import { stringTemplate } from 'netlify-cms-lib-widgets';
|
||||||
|
import { VIEW_STYLE_LIST } from '../constants/collectionViews';
|
||||||
|
|
||||||
const { keyToPathArray } = stringTemplate;
|
const { keyToPathArray } = stringTemplate;
|
||||||
|
|
||||||
@ -58,6 +61,7 @@ let page: number;
|
|||||||
let slug: string;
|
let slug: string;
|
||||||
|
|
||||||
const storageSortKey = 'netlify-cms.entries.sort';
|
const storageSortKey = 'netlify-cms.entries.sort';
|
||||||
|
const viewStyleKey = 'netlify-cms.entries.viewStyle';
|
||||||
type StorageSortObject = SortObject & { index: number };
|
type StorageSortObject = SortObject & { index: number };
|
||||||
type StorageSort = { [collection: string]: { [key: string]: StorageSortObject } };
|
type StorageSort = { [collection: string]: { [key: string]: StorageSortObject } };
|
||||||
|
|
||||||
@ -107,8 +111,30 @@ const persistSort = (sort: Sort | undefined) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const loadViewStyle = once(() => {
|
||||||
|
const viewStyle = localStorage.getItem(viewStyleKey);
|
||||||
|
if (viewStyle) {
|
||||||
|
return viewStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem(viewStyleKey, VIEW_STYLE_LIST);
|
||||||
|
return VIEW_STYLE_LIST;
|
||||||
|
});
|
||||||
|
|
||||||
|
const clearViewStyle = () => {
|
||||||
|
localStorage.removeItem(viewStyleKey);
|
||||||
|
};
|
||||||
|
|
||||||
|
const persistViewStyle = (viewStyle: string | undefined) => {
|
||||||
|
if (viewStyle) {
|
||||||
|
localStorage.setItem(viewStyleKey, viewStyle);
|
||||||
|
} else {
|
||||||
|
clearViewStyle();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const entries = (
|
const entries = (
|
||||||
state = Map({ entities: Map(), pages: Map(), sort: loadSort() }),
|
state = Map({ entities: Map(), pages: Map(), sort: loadSort(), viewStyle: loadViewStyle() }),
|
||||||
action: EntriesAction,
|
action: EntriesAction,
|
||||||
) => {
|
) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
@ -272,6 +298,16 @@ const entries = (
|
|||||||
return newState;
|
return newState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case CHANGE_VIEW_STYLE: {
|
||||||
|
const payload = (action.payload as unknown) as ChangeViewStylePayload;
|
||||||
|
const { style } = payload;
|
||||||
|
const newState = state.withMutations(map => {
|
||||||
|
map.setIn(['viewStyle'], style);
|
||||||
|
});
|
||||||
|
persistViewStyle(newState.get('viewStyle') as string);
|
||||||
|
return newState;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@ -308,6 +344,10 @@ export const selectEntriesFilterFields = (entries: Entries, collection: string)
|
|||||||
return values;
|
return values;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const selectViewStyle = (entries: Entries) => {
|
||||||
|
return entries.get('viewStyle');
|
||||||
|
};
|
||||||
|
|
||||||
export const selectEntry = (state: Entries, collection: string, slug: string) =>
|
export const selectEntry = (state: Entries, collection: string, slug: string) =>
|
||||||
state.getIn(['entities', `${collection}.${slug}`]);
|
state.getIn(['entities', `${collection}.${slug}`]);
|
||||||
|
|
||||||
|
@ -75,6 +75,7 @@ export type Entries = StaticallyTypedRecord<{
|
|||||||
entities: Entities & EntitiesObject;
|
entities: Entities & EntitiesObject;
|
||||||
sort: Sort;
|
sort: Sort;
|
||||||
filter: Filter;
|
filter: Filter;
|
||||||
|
viewStyle: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export type Deploys = StaticallyTypedRecord<{}>;
|
export type Deploys = StaticallyTypedRecord<{}>;
|
||||||
@ -348,6 +349,10 @@ export interface EntriesFilterFailurePayload {
|
|||||||
error: Error;
|
error: Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ChangeViewStylePayload {
|
||||||
|
style: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface EntriesMoveSuccessPayload extends EntryPayload {
|
export interface EntriesMoveSuccessPayload extends EntryPayload {
|
||||||
entries: EntryObject[];
|
entries: EntryObject[];
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user