[Notebook] new entries on brand new notebook not rendered (#3496)

* [Notebook] new entries on brand new notebook not rendered #3488

* Refactored code to 'mutateObject'  from one place only, add page to newly created section immediately,  update entries copy then call mutate to update it inside domainObject.

Co-authored-by: Jamie V <jamie.j.vigliotta@nasa.gov>
Co-authored-by: Andrew Henry <akhenry@gmail.com>
This commit is contained in:
Nikhil 2020-11-19 08:38:14 -08:00 committed by GitHub
parent 59946e89ef
commit d232dacc65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 147 additions and 151 deletions

View File

@ -24,12 +24,12 @@
:default-section-id="defaultSectionId" :default-section-id="defaultSectionId"
:domain-object="internalDomainObject" :domain-object="internalDomainObject"
:page-title="internalDomainObject.configuration.pageTitle" :page-title="internalDomainObject.configuration.pageTitle"
:pages="pages"
:section-title="internalDomainObject.configuration.sectionTitle" :section-title="internalDomainObject.configuration.sectionTitle"
:sections="sections" :sections="sections"
:selected-section="selectedSection"
:sidebar-covers-entries="sidebarCoversEntries" :sidebar-covers-entries="sidebarCoversEntries"
@updatePage="updatePage" @pagesChanged="pagesChanged"
@updateSection="updateSection" @sectionsChanged="sectionsChanged"
@toggleNav="toggleNav" @toggleNav="toggleNav"
/> />
<div class="c-notebook__page-view"> <div class="c-notebook__page-view">
@ -111,7 +111,7 @@ import Search from '@/ui/components/search.vue';
import SearchResults from './SearchResults.vue'; import SearchResults from './SearchResults.vue';
import Sidebar from './Sidebar.vue'; import Sidebar from './Sidebar.vue';
import { clearDefaultNotebook, getDefaultNotebook, setDefaultNotebook, setDefaultNotebookSection, setDefaultNotebookPage } from '../utils/notebook-storage'; import { clearDefaultNotebook, getDefaultNotebook, setDefaultNotebook, setDefaultNotebookSection, setDefaultNotebookPage } from '../utils/notebook-storage';
import { DEFAULT_CLASS, addNotebookEntry, createNewEmbed, getNotebookEntries } from '../utils/notebook-entries'; import { DEFAULT_CLASS, addNotebookEntry, createNewEmbed, getNotebookEntries, mutateObject } from '../utils/notebook-entries';
import objectUtils from 'objectUtils'; import objectUtils from 'objectUtils';
import { throttle } from 'lodash'; import { throttle } from 'lodash';
@ -220,7 +220,7 @@ export default {
return s; return s;
}); });
this.updateSection({ sections }); this.sectionsChanged({ sections });
this.throttledSearchItem(''); this.throttledSearchItem('');
}, },
createNotebookStorageObject() { createNotebookStorageObject() {
@ -309,7 +309,7 @@ export default {
return null; return null;
} }
return this.openmct.objects.get(oldNotebookStorage.notebookMeta.identifier).then(d => d); return this.openmct.objects.get(oldNotebookStorage.notebookMeta.identifier);
}, },
getPage(section, id) { getPage(section, id) {
return section.pages.find(p => p.id === id); return section.pages.find(p => p.id === id);
@ -379,9 +379,6 @@ export default {
return this.sections.find(section => section.isSelected); return this.sections.find(section => section.isSelected);
}, },
mutateObject(key, value) {
this.openmct.objects.mutate(this.internalDomainObject, key, value);
},
navigateToSectionPage() { navigateToSectionPage() {
const { pageId, sectionId } = this.openmct.router.getParams(); const { pageId, sectionId } = this.openmct.router.getParams();
if (!pageId || !sectionId) { if (!pageId || !sectionId) {
@ -398,7 +395,7 @@ export default {
return s; return s;
}); });
this.updateSection({ sections }); this.sectionsChanged({ sections });
}, },
newEntry(embed = null) { newEntry(embed = null) {
this.search = ''; this.search = '';
@ -411,6 +408,24 @@ export default {
orientationChange() { orientationChange() {
this.formatSidebar(); this.formatSidebar();
}, },
pagesChanged({ pages = [], id = null}) {
const selectedSection = this.getSelectedSection();
if (!selectedSection) {
return;
}
selectedSection.pages = pages;
const sections = this.sections.map(section => {
if (section.id === selectedSection.id) {
section = selectedSection;
}
return section;
});
this.sectionsChanged({ sections });
this.updateDefaultNotebookPage(pages, id);
},
removeDefaultClass(domainObject) { removeDefaultClass(domainObject) {
if (!domainObject) { if (!domainObject) {
return; return;
@ -423,7 +438,7 @@ export default {
} }
classList.splice(index, 1); classList.splice(index, 1);
this.openmct.objects.mutate(domainObject, 'classList', classList); mutateObject(this.openmct, domainObject, 'classList', classList);
}, },
searchItem(input) { searchItem(input) {
this.search = input; this.search = input;
@ -440,12 +455,14 @@ export default {
setDefaultNotebook(this.openmct, notebookStorage); setDefaultNotebook(this.openmct, notebookStorage);
} }
if (this.defaultSectionId.length === 0 || this.defaultSectionId !== notebookStorage.section.id) { if (this.defaultSectionId && this.defaultSectionId.length === 0 || this.defaultSectionId !== notebookStorage.section.id) {
this.defaultSectionId = notebookStorage.section.id; this.defaultSectionId = notebookStorage.section.id;
setDefaultNotebookSection(notebookStorage.section);
} }
if (this.defaultPageId.length === 0 || this.defaultPageId !== notebookStorage.page.id) { if (this.defaultPageId && this.defaultPageId.length === 0 || this.defaultPageId !== notebookStorage.page.id) {
this.defaultPageId = notebookStorage.page.id; this.defaultPageId = notebookStorage.page.id;
setDefaultNotebookPage(notebookStorage.page);
} }
}, },
updateDefaultNotebookPage(pages, id) { updateDefaultNotebookPage(pages, id) {
@ -509,29 +526,11 @@ export default {
const notebookEntries = configuration.entries || {}; const notebookEntries = configuration.entries || {};
notebookEntries[this.selectedSection.id][this.selectedPage.id] = entries; notebookEntries[this.selectedSection.id][this.selectedPage.id] = entries;
this.mutateObject('configuration.entries', notebookEntries); mutateObject(this.openmct, this.internalDomainObject, 'configuration.entries', notebookEntries);
}, },
updateInternalDomainObject(domainObject) { updateInternalDomainObject(domainObject) {
this.internalDomainObject = domainObject; this.internalDomainObject = domainObject;
}, },
updatePage({ pages = [], id = null}) {
const selectedSection = this.getSelectedSection();
if (!selectedSection) {
return;
}
selectedSection.pages = pages;
const sections = this.sections.map(section => {
if (section.id === selectedSection.id) {
section = selectedSection;
}
return section;
});
this.updateSection({ sections });
this.updateDefaultNotebookPage(pages, id);
},
updateParams(sections) { updateParams(sections) {
const selectedSection = sections.find(s => s.isSelected); const selectedSection = sections.find(s => s.isSelected);
if (!selectedSection) { if (!selectedSection) {
@ -555,8 +554,8 @@ export default {
pageId pageId
}); });
}, },
updateSection({ sections, id = null }) { sectionsChanged({ sections, id = null }) {
this.mutateObject('configuration.sections', sections); mutateObject(this.openmct, this.internalDomainObject, 'configuration.sections', sections);
this.updateParams(sections); this.updateParams(sections);
this.updateDefaultNotebookSection(sections, id); this.updateDefaultNotebookSection(sections, id);

View File

@ -66,14 +66,10 @@ export default {
} }
} }
}, },
data() {
return {
};
},
methods: { methods: {
deletePage(id) { deletePage(id) {
const selectedSection = this.sections.find(s => s.isSelected); const selectedSection = this.sections.find(s => s.isSelected);
const page = this.pages.filter(p => p.id !== id); const page = this.pages.find(p => p.id !== id);
deleteNotebookEntries(this.openmct, this.domainObject, selectedSection, page); deleteNotebookEntries(this.openmct, this.domainObject, selectedSection, page);
const selectedPage = this.pages.find(p => p.isSelected); const selectedPage = this.pages.find(p => p.isSelected);

View File

@ -53,10 +53,6 @@ export default {
} }
} }
}, },
data() {
return {
};
},
methods: { methods: {
deleteSection(id) { deleteSection(id) {
const section = this.sections.find(s => s.id === id); const section = this.sections.find(s => s.id === id);

View File

@ -18,7 +18,7 @@
:domain-object="domainObject" :domain-object="domainObject"
:sections="sections" :sections="sections"
:section-title="sectionTitle" :section-title="sectionTitle"
@updateSection="updateSection" @updateSection="sectionsChanged"
/> />
</div> </div>
</div> </div>
@ -48,7 +48,7 @@
:sidebar-covers-entries="sidebarCoversEntries" :sidebar-covers-entries="sidebarCoversEntries"
:page-title="pageTitle" :page-title="pageTitle"
@toggleNav="toggleNav" @toggleNav="toggleNav"
@updatePage="updatePage" @updatePage="pagesChanged"
/> />
</div> </div>
</div> </div>
@ -85,13 +85,6 @@ export default {
return {}; return {};
} }
}, },
pages: {
type: Array,
required: true,
default() {
return [];
}
},
pageTitle: { pageTitle: {
type: String, type: String,
default() { default() {
@ -122,9 +115,16 @@ export default {
return { return {
}; };
}, },
computed: {
pages() {
const selectedSection = this.sections.find(section => section.isSelected);
return selectedSection && selectedSection.pages || [];
}
},
watch: { watch: {
pages(newpages) { pages(newPages) {
if (!newpages.length) { if (!newPages.length) {
this.addPage(); this.addPage();
} }
}, },
@ -141,55 +141,79 @@ export default {
}, },
methods: { methods: {
addPage() { addPage() {
const newPage = this.createNewPage();
const pages = this.addNewPage(newPage);
this.pagesChanged({
pages,
id: newPage.id
});
},
addSection() {
const newSection = this.createNewSection();
const sections = this.addNewSection(newSection);
this.sectionsChanged({
sections,
id: newSection.id
});
},
addNewPage(page) {
const pages = this.pages.map(p => {
p.isSelected = false;
return p;
});
return pages.concat(page);
},
addNewSection(section) {
const sections = this.sections.map(s => {
s.isSelected = false;
return s;
});
return sections.concat(section);
},
createNewPage() {
const pageTitle = this.pageTitle; const pageTitle = this.pageTitle;
const id = uuid(); const id = uuid();
const page = {
return {
id, id,
isDefault: false, isDefault: false,
isSelected: true, isSelected: true,
name: `Unnamed ${pageTitle}`, name: `Unnamed ${pageTitle}`,
pageTitle pageTitle
}; };
this.pages.forEach(p => p.isSelected = false);
const pages = this.pages.concat(page);
this.updatePage({
pages,
id
});
}, },
addSection() { createNewSection() {
const sectionTitle = this.sectionTitle; const sectionTitle = this.sectionTitle;
const id = uuid(); const id = uuid();
const section = { const page = this.createNewPage();
const pages = [page];
return {
id, id,
isDefault: false, isDefault: false,
isSelected: true, isSelected: true,
name: `Unnamed ${sectionTitle}`, name: `Unnamed ${sectionTitle}`,
pages: [], pages,
sectionTitle sectionTitle
}; };
this.sections.forEach(s => s.isSelected = false);
const sections = this.sections.concat(section);
this.updateSection({
sections,
id
});
}, },
toggleNav() { toggleNav() {
this.$emit('toggleNav'); this.$emit('toggleNav');
}, },
updatePage({ pages, id }) { pagesChanged({ pages, id }) {
this.$emit('updatePage', { this.$emit('pagesChanged', {
pages, pages,
id id
}); });
}, },
updateSection({ sections, id }) { sectionsChanged({ sections, id }) {
this.$emit('updateSection', { this.$emit('sectionsChanged', {
sections, sections,
id id
}); });

View File

@ -1,5 +1,4 @@
import objectLink from '../../../ui/mixins/object-link'; import objectLink from '../../../ui/mixins/object-link';
export const DEFAULT_CLASS = 'is-notebook-default'; export const DEFAULT_CLASS = 'is-notebook-default';
const TIME_BOUNDS = { const TIME_BOUNDS = {
START_BOUND: 'tc.startBound', START_BOUND: 'tc.startBound',
@ -8,6 +7,29 @@ const TIME_BOUNDS = {
END_DELTA: 'tc.endDelta' END_DELTA: 'tc.endDelta'
}; };
export function addEntryIntoPage(notebookStorage, entries, entry) {
const defaultSection = notebookStorage.section;
const defaultPage = notebookStorage.page;
if (!defaultSection || !defaultPage) {
return;
}
const newEntries = JSON.parse(JSON.stringify(entries));
let section = newEntries[defaultSection.id];
if (!section) {
newEntries[defaultSection.id] = {};
}
let page = newEntries[defaultSection.id][defaultPage.id];
if (!page) {
newEntries[defaultSection.id][defaultPage.id] = [];
}
newEntries[defaultSection.id][defaultPage.id].push(entry);
return newEntries;
}
export function getHistoricLinkInFixedMode(openmct, bounds, historicLink) { export function getHistoricLinkInFixedMode(openmct, bounds, historicLink) {
if (historicLink.includes('tc.mode=fixed')) { if (historicLink.includes('tc.mode=fixed')) {
return historicLink; return historicLink;
@ -38,35 +60,6 @@ export function getHistoricLinkInFixedMode(openmct, bounds, historicLink) {
return params.join('&'); return params.join('&');
} }
export function getNotebookDefaultEntries(notebookStorage, domainObject) {
if (!notebookStorage || !domainObject) {
return null;
}
const defaultSection = notebookStorage.section;
const defaultPage = notebookStorage.page;
if (!defaultSection || !defaultPage) {
return null;
}
const configuration = domainObject.configuration;
const entries = configuration.entries || {};
let section = entries[defaultSection.id];
if (!section) {
section = {};
entries[defaultSection.id] = section;
}
let page = entries[defaultSection.id][defaultPage.id];
if (!page) {
page = [];
entries[defaultSection.id][defaultPage.id] = [];
}
return entries[defaultSection.id][defaultPage.id];
}
export function createNewEmbed(snapshotMeta, snapshot = '') { export function createNewEmbed(snapshotMeta, snapshot = '') {
const { const {
bounds, bounds,
@ -120,24 +113,25 @@ export function addNotebookEntry(openmct, domainObject, notebookStorage, embed =
? [embed] ? [embed]
: []; : [];
const defaultEntries = getNotebookDefaultEntries(notebookStorage, domainObject);
const id = `entry-${date}`; const id = `entry-${date}`;
defaultEntries.push({ const entry = {
id, id,
createdOn: date, createdOn: date,
text: '', text: '',
embeds embeds
}); };
const newEntries = addEntryIntoPage(notebookStorage, entries, entry);
addDefaultClass(domainObject); addDefaultClass(domainObject);
openmct.objects.mutate(domainObject, 'configuration.entries', entries); mutateObject(openmct, domainObject, 'configuration.entries', newEntries);
return id; return id;
} }
export function getNotebookEntries(domainObject, selectedSection, selectedPage) { export function getNotebookEntries(domainObject, selectedSection, selectedPage) {
if (!domainObject || !selectedSection || !selectedPage) { if (!domainObject || !selectedSection || !selectedPage) {
return null; return;
} }
const configuration = domainObject.configuration; const configuration = domainObject.configuration;
@ -145,12 +139,12 @@ export function getNotebookEntries(domainObject, selectedSection, selectedPage)
let section = entries[selectedSection.id]; let section = entries[selectedSection.id];
if (!section) { if (!section) {
return null; return;
} }
let page = entries[selectedSection.id][selectedPage.id]; let page = entries[selectedSection.id][selectedPage.id];
if (!page) { if (!page) {
return null; return;
} }
return entries[selectedSection.id][selectedPage.id]; return entries[selectedSection.id][selectedPage.id];
@ -196,7 +190,11 @@ export function deleteNotebookEntries(openmct, domainObject, selectedSection, se
delete entries[selectedSection.id][selectedPage.id]; delete entries[selectedSection.id][selectedPage.id];
openmct.objects.mutate(domainObject, 'configuration.entries', entries); mutateObject(openmct, domainObject, 'configuration.entries', entries);
}
export function mutateObject(openmct, object, key, value) {
openmct.objects.mutate(object, key, value);
} }
function addDefaultClass(domainObject) { function addDefaultClass(domainObject) {
@ -206,4 +204,6 @@ function addDefaultClass(domainObject) {
} }
classList.push(DEFAULT_CLASS); classList.push(DEFAULT_CLASS);
domainObject.classList = classList;
} }

View File

@ -20,7 +20,7 @@
* at runtime from the About dialog for additional information. * at runtime from the About dialog for additional information.
*****************************************************************************/ *****************************************************************************/
import * as NotebookEntries from './notebook-entries'; import * as NotebookEntries from './notebook-entries';
import { createOpenMct, spyOnBuiltins, resetApplicationState } from 'utils/testing'; import { createOpenMct, resetApplicationState } from 'utils/testing';
const notebookStorage = { const notebookStorage = {
domainObject: { domainObject: {
@ -121,7 +121,6 @@ describe('Notebook Entries:', () => {
beforeEach(done => { beforeEach(done => {
openmct = createOpenMct(); openmct = createOpenMct();
window.localStorage.setItem('notebook-storage', null); window.localStorage.setItem('notebook-storage', null);
spyOnBuiltins(['mutate'], openmct.objects);
done(); done();
}); });
@ -137,24 +136,16 @@ describe('Notebook Entries:', () => {
expect(entries.length).toEqual(0); expect(entries.length).toEqual(0);
}); });
it('addNotebookEntry mutates object', () => { it('addNotebookEntry adds entry', (done) => {
const unlisten = openmct.objects.observe(notebookDomainObject, '*', (object) => {
const entries = NotebookEntries.getNotebookEntries(notebookDomainObject, selectedSection, selectedPage);
expect(entries.length).toEqual(1);
done();
unlisten();
});
NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage); NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage);
expect(openmct.objects.mutate).toHaveBeenCalled();
});
it('addNotebookEntry adds entry', () => {
NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage);
const entries = NotebookEntries.getNotebookEntries(notebookDomainObject, selectedSection, selectedPage);
expect(entries.length).toEqual(1);
});
it('getEntryPosById returns valid position', () => {
const entryId = NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage);
const position = NotebookEntries.getEntryPosById(entryId, notebookDomainObject, selectedSection, selectedPage);
expect(position).toEqual(0);
}); });
it('getEntryPosById returns valid position', () => { it('getEntryPosById returns valid position', () => {
@ -174,22 +165,13 @@ describe('Notebook Entries:', () => {
expect(success).toBe(true); expect(success).toBe(true);
}); });
it('deleteNotebookEntries mutates object', () => { it('deleteNotebookEntries deletes correct page entries', () => {
openmct.objects.mutate.calls.reset();
NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage);
NotebookEntries.deleteNotebookEntries(openmct, notebookDomainObject, selectedSection, selectedPage);
expect(openmct.objects.mutate).toHaveBeenCalledTimes(2);
});
it('deleteNotebookEntries deletes correct entry', () => {
NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage); NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage);
NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage); NotebookEntries.addNotebookEntry(openmct, notebookDomainObject, notebookStorage);
NotebookEntries.deleteNotebookEntries(openmct, notebookDomainObject, selectedSection, selectedPage); NotebookEntries.deleteNotebookEntries(openmct, notebookDomainObject, selectedSection, selectedPage);
const afterEntries = NotebookEntries.getNotebookEntries(notebookDomainObject, selectedSection, selectedPage); const afterEntries = NotebookEntries.getNotebookEntries(notebookDomainObject, selectedSection, selectedPage);
expect(afterEntries).toEqual(null); expect(afterEntries).toEqual(undefined);
}); });
}); });

View File

@ -18,7 +18,9 @@
title="This item is missing" title="This item is missing"
></span> ></span>
</div> </div>
<div class="c-tree__item__name c-object-label__name"> <div class="c-tree__item__name c-object-label__name"
:class="classList"
>
{{ observedObject.name }} {{ observedObject.name }}
</div> </div>
</a> </a>

View File

@ -9,10 +9,7 @@
></button> ></button>
<div <div
class="l-browse-bar__object-name--w c-object-label" class="l-browse-bar__object-name--w c-object-label"
:class="{ :class="[classList, { 'is-missing': domainObject.status === 'missing' }]"
classList,
'is-missing': domainObject.status === 'missing'
}"
> >
<div class="c-object-label__type-icon" <div class="c-object-label__type-icon"
:class="type.cssClass" :class="type.cssClass"