From 72fd39b494399e8a97024cc7d21b66a8eae84c55 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Tue, 27 Jun 2017 20:59:53 -0700 Subject: [PATCH] DocumentsStore WIP --- __mocks__/localStorage.js | 20 ++++++ frontend/models/Document.js | 8 +-- frontend/models/Document.test.js | 13 ++++ frontend/stores/CollectionsStore.js | 2 +- frontend/stores/CollectionsStore.test.js | 4 +- frontend/stores/DocumentsStore.js | 83 ++++++++++++++++++++++++ frontend/stores/UiStore.js | 2 +- frontend/utils/setupJest.js | 2 + 8 files changed, 125 insertions(+), 9 deletions(-) create mode 100644 __mocks__/localStorage.js create mode 100644 frontend/models/Document.test.js create mode 100644 frontend/stores/DocumentsStore.js diff --git a/__mocks__/localStorage.js b/__mocks__/localStorage.js new file mode 100644 index 00000000..3d8394b9 --- /dev/null +++ b/__mocks__/localStorage.js @@ -0,0 +1,20 @@ +const storage = {}; + +export default { + setItem: function(key, value) { + storage[key] = value || ''; + }, + getItem: function(key) { + return key in storage ? storage[key] : null; + }, + removeItem: function(key) { + delete storage[key]; + }, + get length() { + return Object.keys(storage).length; + }, + key: function(i) { + var keys = Object.keys(storage); + return keys[i] || null; + }, +}; diff --git a/frontend/models/Document.js b/frontend/models/Document.js index 6f98addd..77dc3419 100644 --- a/frontend/models/Document.js +++ b/frontend/models/Document.js @@ -2,7 +2,7 @@ import { extendObservable, action, runInAction, computed } from 'mobx'; import invariant from 'invariant'; -import ApiClient, { client } from 'utils/ApiClient'; +import { client } from 'utils/ApiClient'; import stores from 'stores'; import ErrorsStore from 'stores/ErrorsStore'; @@ -17,14 +17,13 @@ class Document { html: string; id: string; private: boolean; + starred: boolean; team: string; text: string; title: string; updatedAt: string; updatedBy: User; url: string; - - client: ApiClient; errors: ErrorsStore; /* Computed */ @@ -56,7 +55,7 @@ class Document { @action update = async () => { try { - const res = await this.client.post('/documents.info', { id: this.id }); + const res = await client.post('/documents.info', { id: this.id }); invariant(res && res.data, 'Document API response should be available'); const { data } = res; runInAction('Document#update', () => { @@ -73,7 +72,6 @@ class Document { constructor(document: Document) { this.updateData(document); - this.client = client; this.errors = stores.errors; } } diff --git a/frontend/models/Document.test.js b/frontend/models/Document.test.js new file mode 100644 index 00000000..e7db05b7 --- /dev/null +++ b/frontend/models/Document.test.js @@ -0,0 +1,13 @@ +/* eslint-disable */ +import Document from './Document'; + +describe('Document model', () => { + test('should initialize with data', () => { + const document = new Document({ + id: 123, + title: 'Onboarding', + text: 'Some body text' + }); + expect(document.title).toBe('Onboarding'); + }); +}); diff --git a/frontend/stores/CollectionsStore.js b/frontend/stores/CollectionsStore.js index 2f9d2daf..ae8e2644 100644 --- a/frontend/stores/CollectionsStore.js +++ b/frontend/stores/CollectionsStore.js @@ -22,7 +22,7 @@ class CollectionsStore { /* Actions */ - @action fetch = async (): Promise<*> => { + @action fetchAll = async (): Promise<*> => { try { const res = await this.client.post('/collections.list', { id: this.teamId, diff --git a/frontend/stores/CollectionsStore.test.js b/frontend/stores/CollectionsStore.test.js index 2694a9dc..ef39ccbe 100644 --- a/frontend/stores/CollectionsStore.test.js +++ b/frontend/stores/CollectionsStore.test.js @@ -27,7 +27,7 @@ describe('CollectionsStore', () => { })), }; - await store.fetch(); + await store.fetchAll(); expect(store.client.post).toHaveBeenCalledWith('/collections.list', { id: 123, @@ -44,7 +44,7 @@ describe('CollectionsStore', () => { add: jest.fn(), }; - await store.fetch(); + await store.fetchAll(); expect(store.errors.add).toHaveBeenCalledWith( 'Failed to load collections' diff --git a/frontend/stores/DocumentsStore.js b/frontend/stores/DocumentsStore.js new file mode 100644 index 00000000..250d107b --- /dev/null +++ b/frontend/stores/DocumentsStore.js @@ -0,0 +1,83 @@ +// @flow +import { observable, action, runInAction } from 'mobx'; +import { client } from 'utils/ApiClient'; +import _ from 'lodash'; +import invariant from 'invariant'; + +import stores from 'stores'; +import Document from 'models/Document'; +import ErrorsStore from 'stores/ErrorsStore'; + +type Options = { + teamId: string, +}; + +class DocumentsStore { + @observable data: Object = {}; + @observable isLoaded: boolean = false; + errors: ErrorsStore; + + /* Actions */ + + @action fetchAll = async (): Promise<*> => { + try { + const res = await client.post('/documents.list'); + invariant(res && res.data, 'Document list not available'); + const { data } = res; + runInAction('DocumentsStore#fetchAll', () => { + const loaded = _.keyBy( + data.map(document => new Document(document)), + 'id' + ); + this.data = { + ...this.data, + ...loaded, + }; + this.isLoaded = true; + }); + } catch (e) { + this.errors.add('Failed to load documents'); + } + }; + + @action fetch = async (id: string): Promise<*> => { + try { + const res = await client.post('/documents.info', { id }); + invariant(res && res.data, 'Document not available'); + const { data } = res; + runInAction('DocumentsStore#fetch', () => { + this.update(new Document(data)); + this.isLoaded = true; + }); + } catch (e) { + this.errors.add('Failed to load documents'); + } + }; + + @action add = (document: Document): void => { + this.data[document.id] = document; + }; + + @action remove = (id: string): void => { + delete this.data[id]; + }; + + @action update = (document: Document): void => { + const existing = this.data[document.id]; + + this.data[document.id] = { + ...existing, + document, + }; + }; + + getById = (id: string): Document => { + return _.find(this.data, { id }); + }; + + constructor(options: Options) { + this.errors = stores.errors; + } +} + +export default DocumentsStore; diff --git a/frontend/stores/UiStore.js b/frontend/stores/UiStore.js index 74068d0d..48327a7b 100644 --- a/frontend/stores/UiStore.js +++ b/frontend/stores/UiStore.js @@ -1,6 +1,6 @@ // @flow import { observable, action, computed } from 'mobx'; -import type { Document } from 'types'; +import Document from 'models/Document'; import Collection from 'models/Collection'; class UiStore { diff --git a/frontend/utils/setupJest.js b/frontend/utils/setupJest.js index dcd1f075..61b717c2 100644 --- a/frontend/utils/setupJest.js +++ b/frontend/utils/setupJest.js @@ -2,10 +2,12 @@ import React from 'react'; import { shallow } from 'enzyme'; import toJson from 'enzyme-to-json'; +import localStorage from '../../__mocks__/localStorage'; const snap = children => { const wrapper = shallow(children); expect(toJson(wrapper)).toMatchSnapshot(); }; +global.localStorage = localStorage; global.snap = snap;