Skip to content
This repository was archived by the owner on Sep 9, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 28 additions & 0 deletions packages/core/dev-test/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,34 @@ collections:
- label: Description
name: description
widget: text
- name: string_list
label: String List
widget: list
fields:
- label: Tag
name: tag
widget: string
- name: number_list
label: Number List
widget: list
default:
- 5
- 13
- 2
fields:
- label: Value
name: value
widget: number
- name: boolean_list
label: Boolean List
widget: list
default:
- false
- true
fields:
- label: Active
name: active
widget: boolean
- name: typed_list
label: Typed List
widget: list
Expand Down
2 changes: 2 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,10 @@
"@emotion/jest": "11.10.5",
"@pmmmwh/react-refresh-webpack-plugin": "0.5.10",
"@simbathesailor/use-what-changed": "2.0.0",
"@testing-library/dom": "8.19.1",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "13.4.0",
"@testing-library/user-event": "14.4.3",
"@types/common-tags": "1.8.1",
"@types/create-react-class": "15.6.3",
"@types/fs-extra": "11.0.1",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/__mocks__/array-move.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default jest.fn();
2 changes: 2 additions & 0 deletions packages/core/src/__mocks__/history.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/* eslint-disable import/prefer-default-export */
export const createHashHistory = jest.fn();
7 changes: 1 addition & 6 deletions packages/core/src/actions/auth.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import { currentBackend } from '../backend';
import { AUTH_FAILURE, AUTH_REQUEST, AUTH_REQUEST_DONE, AUTH_SUCCESS, LOGOUT } from '../constants';
import { addSnackbar } from '../store/slices/snackbars';

import type { AnyAction } from 'redux';
import type { ThunkDispatch } from 'redux-thunk';
import type { Credentials, User } from '../interface';
import type { RootState } from '../store';

export const AUTH_REQUEST = 'AUTH_REQUEST';
export const AUTH_SUCCESS = 'AUTH_SUCCESS';
export const AUTH_FAILURE = 'AUTH_FAILURE';
export const AUTH_REQUEST_DONE = 'AUTH_REQUEST_DONE';
export const LOGOUT = 'LOGOUT';

export function authenticating() {
return {
type: AUTH_REQUEST,
Expand Down
7 changes: 2 additions & 5 deletions packages/core/src/actions/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import trimStart from 'lodash/trimStart';
import yaml from 'yaml';

import { resolveBackend } from '../backend';
import { CONFIG_FAILURE, CONFIG_REQUEST, CONFIG_SUCCESS } from '../constants';
import validateConfig from '../constants/configSchema';
import { I18N, I18N_FIELD, I18N_STRUCTURE } from '../lib/i18n';
import { selectDefaultSortableFields } from '../lib/util/collection.util';
Expand All @@ -23,10 +24,6 @@ import type {
} from '../interface';
import type { RootState } from '../store';

export const CONFIG_REQUEST = 'CONFIG_REQUEST';
export const CONFIG_SUCCESS = 'CONFIG_SUCCESS';
export const CONFIG_FAILURE = 'CONFIG_FAILURE';

function isObjectField<F extends BaseField = UnknownField>(field: Field<F>): field is ObjectField {
return 'fields' in (field as ObjectField);
}
Expand Down Expand Up @@ -125,7 +122,7 @@ function throwOnMissingDefaultLocale(i18n?: I18nInfo) {
}

export function applyDefaults(originalConfig: Config) {
return produce(originalConfig, config => {
return produce(originalConfig, (config: Config) => {
config.slug = config.slug || {};
config.collections = config.collections || [];

Expand Down
108 changes: 47 additions & 61 deletions packages/core/src/actions/entries.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,53 @@
import isEqual from 'lodash/isEqual';

import { currentBackend } from '../backend';
import { SORT_DIRECTION_ASCENDING } from '../constants';
import {
ADD_DRAFT_ENTRY_MEDIA_FILE,
CHANGE_VIEW_STYLE,
DRAFT_CHANGE_FIELD,
DRAFT_CREATE_DUPLICATE_FROM_ENTRY,
DRAFT_CREATE_EMPTY,
DRAFT_CREATE_FROM_ENTRY,
DRAFT_CREATE_FROM_LOCAL_BACKUP,
DRAFT_DISCARD,
DRAFT_LOCAL_BACKUP_DELETE,
DRAFT_LOCAL_BACKUP_RETRIEVED,
DRAFT_VALIDATION_ERRORS,
ENTRIES_FAILURE,
ENTRIES_REQUEST,
ENTRIES_SUCCESS,
ENTRY_DELETE_FAILURE,
ENTRY_DELETE_REQUEST,
ENTRY_DELETE_SUCCESS,
ENTRY_FAILURE,
ENTRY_PERSIST_FAILURE,
ENTRY_PERSIST_REQUEST,
ENTRY_PERSIST_SUCCESS,
ENTRY_REQUEST,
ENTRY_SUCCESS,
FILTER_ENTRIES_FAILURE,
FILTER_ENTRIES_REQUEST,
FILTER_ENTRIES_SUCCESS,
GROUP_ENTRIES_FAILURE,
GROUP_ENTRIES_REQUEST,
GROUP_ENTRIES_SUCCESS,
REMOVE_DRAFT_ENTRY_MEDIA_FILE,
SORT_DIRECTION_ASCENDING,
SORT_ENTRIES_FAILURE,
SORT_ENTRIES_REQUEST,
SORT_ENTRIES_SUCCESS,
} from '../constants';
import ValidationErrorTypes from '../constants/validationErrorTypes';
import { duplicateDefaultI18nFields, hasI18n, I18N_FIELD, serializeI18n } from '../lib/i18n';
import { serializeValues } from '../lib/serializeEntryValues';
import { Cursor } from '../lib/util';
import { selectFields, updateFieldByKey } from '../lib/util/collection.util';
import { selectPublishedSlugs } from '../reducers';
import { selectCollectionEntriesCursor } from '../reducers/cursors';
import { selectEntriesSortFields, selectIsFetching } from '../reducers/entries';
import { selectCollectionEntriesCursor } from '../reducers/selectors/cursors';
import {
selectEntriesSortFields,
selectIsFetching,
selectPublishedSlugs,
} from '../reducers/selectors/entries';
import { navigateToEntry } from '../routing/history';
import { addSnackbar } from '../store/slices/snackbars';
import { createAssetProxy } from '../valueObjects/AssetProxy';
Expand Down Expand Up @@ -40,52 +78,6 @@ import type {
import type { RootState } from '../store';
import type AssetProxy from '../valueObjects/AssetProxy';

/*
* Constant Declarations
*/
export const ENTRY_REQUEST = 'ENTRY_REQUEST';
export const ENTRY_SUCCESS = 'ENTRY_SUCCESS';
export const ENTRY_FAILURE = 'ENTRY_FAILURE';

export const ENTRIES_REQUEST = 'ENTRIES_REQUEST';
export const ENTRIES_SUCCESS = 'ENTRIES_SUCCESS';
export const ENTRIES_FAILURE = 'ENTRIES_FAILURE';

export const SORT_ENTRIES_REQUEST = 'SORT_ENTRIES_REQUEST';
export const SORT_ENTRIES_SUCCESS = 'SORT_ENTRIES_SUCCESS';
export const SORT_ENTRIES_FAILURE = 'SORT_ENTRIES_FAILURE';

export const FILTER_ENTRIES_REQUEST = 'FILTER_ENTRIES_REQUEST';
export const FILTER_ENTRIES_SUCCESS = 'FILTER_ENTRIES_SUCCESS';
export const FILTER_ENTRIES_FAILURE = 'FILTER_ENTRIES_FAILURE';

export const GROUP_ENTRIES_REQUEST = 'GROUP_ENTRIES_REQUEST';
export const GROUP_ENTRIES_SUCCESS = 'GROUP_ENTRIES_SUCCESS';
export const GROUP_ENTRIES_FAILURE = 'GROUP_ENTRIES_FAILURE';

export const DRAFT_CREATE_FROM_ENTRY = 'DRAFT_CREATE_FROM_ENTRY';
export const DRAFT_CREATE_EMPTY = 'DRAFT_CREATE_EMPTY';
export const DRAFT_DISCARD = 'DRAFT_DISCARD';
export const DRAFT_CHANGE_FIELD = 'DRAFT_CHANGE_FIELD';
export const DRAFT_VALIDATION_ERRORS = 'DRAFT_VALIDATION_ERRORS';
export const DRAFT_LOCAL_BACKUP_RETRIEVED = 'DRAFT_LOCAL_BACKUP_RETRIEVED';
export const DRAFT_LOCAL_BACKUP_DELETE = 'DRAFT_LOCAL_BACKUP_DELETE';
export const DRAFT_CREATE_FROM_LOCAL_BACKUP = 'DRAFT_CREATE_FROM_LOCAL_BACKUP';
export const DRAFT_CREATE_DUPLICATE_FROM_ENTRY = 'DRAFT_CREATE_DUPLICATE_FROM_ENTRY';

export const ENTRY_PERSIST_REQUEST = 'ENTRY_PERSIST_REQUEST';
export const ENTRY_PERSIST_SUCCESS = 'ENTRY_PERSIST_SUCCESS';
export const ENTRY_PERSIST_FAILURE = 'ENTRY_PERSIST_FAILURE';

export const ENTRY_DELETE_REQUEST = 'ENTRY_DELETE_REQUEST';
export const ENTRY_DELETE_SUCCESS = 'ENTRY_DELETE_SUCCESS';
export const ENTRY_DELETE_FAILURE = 'ENTRY_DELETE_FAILURE';

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 CHANGE_VIEW_STYLE = 'CHANGE_VIEW_STYLE';

/*
* Simple Action Creators (Internal)
* We still need to export them for tests
Expand Down Expand Up @@ -287,7 +279,7 @@ export function sortByField(
return async (dispatch: ThunkDispatch<RootState, {}, AnyAction>, getState: () => RootState) => {
const state = getState();
// if we're already fetching we update the sort key, but skip loading entries
const isFetching = selectIsFetching(state.entries, collection.name);
const isFetching = selectIsFetching(state, collection.name);
dispatch(sortEntriesRequest(collection, key, direction));
if (isFetching) {
return;
Expand All @@ -307,7 +299,7 @@ export function filterByField(collection: Collection, filter: ViewFilter) {
return async (dispatch: ThunkDispatch<RootState, {}, AnyAction>, getState: () => RootState) => {
const state = getState();
// if we're already fetching we update the filter key, but skip loading entries
const isFetching = selectIsFetching(state.entries, collection.name);
const isFetching = selectIsFetching(state, collection.name);
dispatch(filterEntriesRequest(collection, filter));
if (isFetching) {
return;
Expand All @@ -325,7 +317,7 @@ export function filterByField(collection: Collection, filter: ViewFilter) {
export function groupByField(collection: Collection, group: ViewGroup) {
return async (dispatch: ThunkDispatch<RootState, {}, AnyAction>, getState: () => RootState) => {
const state = getState();
const isFetching = selectIsFetching(state.entries, collection.name);
const isFetching = selectIsFetching(state, collection.name);
dispatch({
type: GROUP_ENTRIES_REQUEST,
payload: {
Expand Down Expand Up @@ -657,7 +649,7 @@ export function loadEntries(collection: Collection, page = 0) {
return;
}
const state = getState();
const sortFields = selectEntriesSortFields(state.entries, collection.name);
const sortFields = selectEntriesSortFields(state, collection.name);
if (sortFields && sortFields.length > 0) {
const field = sortFields[0];
return dispatch(sortByField(collection, field.key, field.direction));
Expand Down Expand Up @@ -687,12 +679,6 @@ export function loadEntries(collection: Collection, page = 0) {

const cleanResponse = {
...response,
// The only existing backend using the pagination system is the
// Algolia integration, which is also the only integration used
// to list entries. Thus, this checking for an integration can
// determine whether or not this is using the old integer-based
// pagination API. Other backends will simply store an empty
// cursor, which behaves identically to no cursor at all.
cursor: !('cursor' in response && response.cursor)
? Cursor.create({
actions: ['next'],
Expand Down Expand Up @@ -759,7 +745,7 @@ export function traverseCollectionCursor(collection: Collection, action: string)

const { action: realAction, append } =
action in appendActions ? appendActions[action] : { action, append: false };
const cursor = selectCollectionEntriesCursor(state.cursors, collection.name);
const cursor = selectCollectionEntriesCursor(state, collection.name);

// Handle cursors representing pages in the old, integer-based pagination API
if (cursor.meta?.usingOldPaginationAPI ?? false) {
Expand Down
18 changes: 9 additions & 9 deletions packages/core/src/actions/media.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import {
ADD_ASSET,
ADD_ASSETS,
LOAD_ASSET_FAILURE,
LOAD_ASSET_REQUEST,
LOAD_ASSET_SUCCESS,
REMOVE_ASSET,
} from '../constants';
import { isAbsolutePath } from '../lib/util';
import { selectMediaFilePath } from '../lib/util/media.util';
import { selectMediaFileByPath } from '../reducers/mediaLibrary';
import { selectMediaFileByPath } from '../reducers/selectors/mediaLibrary';
import { createAssetProxy } from '../valueObjects/AssetProxy';
import { getMediaDisplayURL, getMediaFile, waitForMediaLibraryToLoad } from './mediaLibrary';

Expand All @@ -10,14 +18,6 @@ import type { BaseField, Collection, Entry, Field, UnknownField } from '../inter
import type { RootState } from '../store';
import type AssetProxy from '../valueObjects/AssetProxy';

export const ADD_ASSETS = 'ADD_ASSETS';
export const ADD_ASSET = 'ADD_ASSET';
export const REMOVE_ASSET = 'REMOVE_ASSET';

export const LOAD_ASSET_REQUEST = 'LOAD_ASSET_REQUEST';
export const LOAD_ASSET_SUCCESS = 'LOAD_ASSET_SUCCESS';
export const LOAD_ASSET_FAILURE = 'LOAD_ASSET_FAILURE';

export function addAssets(assets: AssetProxy[]) {
return { type: ADD_ASSETS, payload: assets } as const;
}
Expand Down
45 changes: 23 additions & 22 deletions packages/core/src/actions/mediaLibrary.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
import { currentBackend } from '../backend';
import confirm from '../components/UI/Confirm';
import {
MEDIA_DELETE_FAILURE,
MEDIA_DELETE_REQUEST,
MEDIA_DELETE_SUCCESS,
MEDIA_DISPLAY_URL_FAILURE,
MEDIA_DISPLAY_URL_REQUEST,
MEDIA_DISPLAY_URL_SUCCESS,
MEDIA_INSERT,
MEDIA_LIBRARY_CLOSE,
MEDIA_LIBRARY_CREATE,
MEDIA_LIBRARY_OPEN,
MEDIA_LOAD_FAILURE,
MEDIA_LOAD_REQUEST,
MEDIA_LOAD_SUCCESS,
MEDIA_PERSIST_FAILURE,
MEDIA_PERSIST_REQUEST,
MEDIA_PERSIST_SUCCESS,
MEDIA_REMOVE_INSERTED,
} from '../constants';
import { sanitizeSlug } from '../lib/urlHelper';
import { basename, getBlobSHA } from '../lib/util';
import { selectMediaFilePath, selectMediaFilePublicPath } from '../lib/util/media.util';
import { selectEditingDraft } from '../reducers/entries';
import { selectMediaDisplayURL, selectMediaFiles } from '../reducers/mediaLibrary';
import { selectEditingDraft } from '../reducers/selectors/entryDraft';
import { selectMediaDisplayURL, selectMediaFiles } from '../reducers/selectors/mediaLibrary';
import { addSnackbar } from '../store/slices/snackbars';
import { createAssetProxy } from '../valueObjects/AssetProxy';
import { addDraftEntryMediaFile, removeDraftEntryMediaFile } from './entries';
Expand All @@ -25,24 +44,6 @@ import type {
import type { RootState } from '../store';
import type AssetProxy from '../valueObjects/AssetProxy';

export const MEDIA_LIBRARY_OPEN = 'MEDIA_LIBRARY_OPEN';
export const MEDIA_LIBRARY_CLOSE = 'MEDIA_LIBRARY_CLOSE';
export const MEDIA_LIBRARY_CREATE = 'MEDIA_LIBRARY_CREATE';
export const MEDIA_INSERT = 'MEDIA_INSERT';
export const MEDIA_REMOVE_INSERTED = 'MEDIA_REMOVE_INSERTED';
export const MEDIA_LOAD_REQUEST = 'MEDIA_LOAD_REQUEST';
export const MEDIA_LOAD_SUCCESS = 'MEDIA_LOAD_SUCCESS';
export const MEDIA_LOAD_FAILURE = 'MEDIA_LOAD_FAILURE';
export const MEDIA_PERSIST_REQUEST = 'MEDIA_PERSIST_REQUEST';
export const MEDIA_PERSIST_SUCCESS = 'MEDIA_PERSIST_SUCCESS';
export const MEDIA_PERSIST_FAILURE = 'MEDIA_PERSIST_FAILURE';
export const MEDIA_DELETE_REQUEST = 'MEDIA_DELETE_REQUEST';
export const MEDIA_DELETE_SUCCESS = 'MEDIA_DELETE_SUCCESS';
export const MEDIA_DELETE_FAILURE = 'MEDIA_DELETE_FAILURE';
export const MEDIA_DISPLAY_URL_REQUEST = 'MEDIA_DISPLAY_URL_REQUEST';
export const MEDIA_DISPLAY_URL_SUCCESS = 'MEDIA_DISPLAY_URL_SUCCESS';
export const MEDIA_DISPLAY_URL_FAILURE = 'MEDIA_DISPLAY_URL_FAILURE';

export function createMediaLibrary(instance: MediaLibraryInstance) {
const api = {
show: instance.show || (() => undefined),
Expand Down Expand Up @@ -219,7 +220,7 @@ export function persistMedia(file: File, opts: MediaOptions = {}) {
const fileName = sanitizeSlug(file.name.toLowerCase(), config.slug);
const existingFile = files.find(existingFile => existingFile.name.toLowerCase() === fileName);

const editingDraft = selectEditingDraft(state.entryDraft);
const editingDraft = selectEditingDraft(state);

/**
* Check for existing files of the same name before persisting. If no asset
Expand Down Expand Up @@ -308,7 +309,7 @@ export function deleteMedia(file: MediaFile) {
dispatch(removeAsset(file.path));
dispatch(removeDraftEntryMediaFile({ id: file.id }));
} else {
const editingDraft = selectEditingDraft(state.entryDraft);
const editingDraft = selectEditingDraft(state);

dispatch(mediaDeleting());
dispatch(removeAsset(file.path));
Expand Down
7 changes: 2 additions & 5 deletions packages/core/src/actions/scroll.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { SCROLL_SYNC_ENABLED, SET_SCROLL, TOGGLE_SCROLL } from '../constants';

import type { AnyAction } from 'redux';
import type { ThunkDispatch } from 'redux-thunk';
import type { RootState } from '../store';

export const SCROLL_SYNC_ENABLED = 'cms.scroll-sync-enabled';

export const TOGGLE_SCROLL = 'TOGGLE_SCROLL';
export const SET_SCROLL = 'SET_SCROLL';

export function togglingScroll() {
return {
type: TOGGLE_SCROLL,
Expand Down
Loading