From 6392936573fd09429295b44a3400ea4ba664fdca Mon Sep 17 00:00:00 2001 From: Tahsin Islam Date: Tue, 5 Oct 2021 15:08:45 -0400 Subject: [PATCH 01/47] when giving module access, no longer shows people who already have access --- .../people-search-dialog.test.js.snap | 180 ++++++++++++++++++ .../components/module-permissions-dialog.jsx | 49 +++-- .../module-permissions-dialog.test.js | 11 +- .../components/people-search-dialog.jsx | 28 ++- .../components/people-search-dialog.test.js | 48 ++++- 5 files changed, 289 insertions(+), 27 deletions(-) diff --git a/packages/app/obojobo-repository/shared/components/__snapshots__/people-search-dialog.test.js.snap b/packages/app/obojobo-repository/shared/components/__snapshots__/people-search-dialog.test.js.snap index d135302c37..451769e57f 100644 --- a/packages/app/obojobo-repository/shared/components/__snapshots__/people-search-dialog.test.js.snap +++ b/packages/app/obojobo-repository/shared/components/__snapshots__/people-search-dialog.test.js.snap @@ -132,6 +132,186 @@ exports[`PeopleSearchDialog renders with people 1`] = ` > + +
+
+ + +
  • +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    + +
  • +
  • +
    +
    + +
    +
    +
    +
    + + + + (me) + +
    +
    +
    + +
  • + + + +`; + +exports[`PeopleSearchDialog renders with people and exludes people with draft permission 1`] = ` +
    +
    + +

    + Find Users to Share With +

    +
    + People who can edit this module +
    + +
    +
    +
      +
    • +
      +
      + +
      +
      +
      +
      + + +
      +
      +
      + +
    • +
    • +
      +
      + +
      +
      +
      +
      + +
      - - - ) + if (this.props.draftPermissions[this.props.draftId] != null) { + return ( + + + + ) + } else { + return ( + + + + ) + } } return null } diff --git a/packages/app/obojobo-repository/shared/components/module-permissions-dialog.test.js b/packages/app/obojobo-repository/shared/components/module-permissions-dialog.test.js index 1905c1e1fd..3784061463 100644 --- a/packages/app/obojobo-repository/shared/components/module-permissions-dialog.test.js +++ b/packages/app/obojobo-repository/shared/components/module-permissions-dialog.test.js @@ -26,11 +26,12 @@ describe('ModulePermissionsDialog', () => { draftId: 'mockDraftId', title: 'Mock Module Title', currentUserId: 99, - draftPermissions: {}, + draftPermissions: { items: [{ id: 99 }] }, loadUsersForModule: jest.fn(), addUserToModule: jest.fn(), deleteModulePermissions: jest.fn(), - onClose: jest.fn() + onClose: jest.fn(), + openPeoplePicker: jest.fn() } }) @@ -75,13 +76,17 @@ describe('ModulePermissionsDialog', () => { defaultProps.draftPermissions['mockDraftId'] = { items: [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 99 }] } + let component + act(() => { component = create() }) + act(() => { + component.root.findByProps({ id: 'modulePermissionsDialog-addPeopleButton' }).props.onClick() + }) expectLoadUsersForModuleToBeCalledOnceWithId() - const peopleListItems = component.root.findAllByType(PeopleListItem) expect(peopleListItems.length).toBe(4) expect(peopleListItems[0].props.isMe).toBe(false) diff --git a/packages/app/obojobo-repository/shared/components/people-search-dialog.jsx b/packages/app/obojobo-repository/shared/components/people-search-dialog.jsx index 841696d855..fe22c96523 100644 --- a/packages/app/obojobo-repository/shared/components/people-search-dialog.jsx +++ b/packages/app/obojobo-repository/shared/components/people-search-dialog.jsx @@ -35,13 +35,27 @@ const PeopleSearchDialog = props => {
        - {props.people.map(p => ( - - - - ))} + {props.people.map(function(p) { + if (props.draftPermissions) { + if (props.draftPermissions.every(d => d.id !== p.id)) { + return ( + + + + ) + } + } else { + return ( + + + + ) + } + })}
      diff --git a/packages/app/obojobo-repository/shared/components/people-search-dialog.test.js b/packages/app/obojobo-repository/shared/components/people-search-dialog.test.js index 40e299ec5f..19658e0e5e 100644 --- a/packages/app/obojobo-repository/shared/components/people-search-dialog.test.js +++ b/packages/app/obojobo-repository/shared/components/people-search-dialog.test.js @@ -15,6 +15,7 @@ describe('PeopleSearchDialog', () => { defaultProps = { currentUserId: 99, people: [], + draftPermissions: null, clearPeopleSearchResults: jest.fn(), onSearchChange: jest.fn(), onSelectPerson: jest.fn(), @@ -36,7 +37,7 @@ describe('PeopleSearchDialog', () => { }) test('renders with people', () => { - defaultProps.people = [{ id: 1 }, { id: 2 }, { id: 99 }] + defaultProps.people = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 99 }] let component act(() => { component = create() @@ -45,9 +46,30 @@ describe('PeopleSearchDialog', () => { expect(defaultProps.clearPeopleSearchResults).toHaveBeenCalledTimes(1) const peopleListItems = component.root.findAllByType(PeopleListItem) - expect(peopleListItems.length).toBe(3) + expect(peopleListItems.length).toBe(4) // PeopleListItems have an 'isMe' prop which should only be true if the id // provided is the same as the current user's id + expect(peopleListItems[0].props.isMe).toBe(false) + expect(peopleListItems[1].props.isMe).toBe(false) + expect(peopleListItems[2].props.isMe).toBe(false) + expect(peopleListItems[3].props.isMe).toBe(true) + + expect(component.toJSON()).toMatchSnapshot() + }) + + test('renders with people and exludes people with draft permission', () => { + defaultProps.people = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 99 }] + defaultProps.draftPermissions = [{ id: 1 }] + let component + act(() => { + component = create() + }) + + expect(defaultProps.clearPeopleSearchResults).toHaveBeenCalledTimes(1) + + const peopleListItems = component.root.findAllByType(PeopleListItem) + expect(peopleListItems.length).toBe(3) + expect(peopleListItems[0].props.isMe).toBe(false) expect(peopleListItems[1].props.isMe).toBe(false) expect(peopleListItems[2].props.isMe).toBe(true) @@ -69,6 +91,28 @@ describe('PeopleSearchDialog', () => { test('clicking a PeopleListItem "Select" button should call onSelectPerson and onClose', () => { defaultProps.people = [{ id: 1 }] + defaultProps.draftPermissions = [] + + let component + act(() => { + component = create() + }) + + expect(defaultProps.clearPeopleSearchResults).toHaveBeenCalledTimes(1) + + component.root + .findByType(PeopleListItem) + .findByProps({ className: 'select-button' }) + .props.onClick() + expect(defaultProps.onSelectPerson).toBeCalledTimes(1) + expect(defaultProps.onSelectPerson).toBeCalledWith({ id: 1 }) + + expect(defaultProps.onClose).toHaveBeenCalledTimes(1) + }) + + test('clicking a PeopleListItem "Select" button should call onSelectPerson and onClose if draftPermission is null', () => { + defaultProps.people = [{ id: 1 }] + let component act(() => { component = create() From 048816cc3e1a140e4c5335620419242bb44a84fd Mon Sep 17 00:00:00 2001 From: Walid Islam Date: Tue, 12 Oct 2021 16:59:08 -0400 Subject: [PATCH 02/47] Cleaned up code, added new test --- .../components/module-permissions-dialog.jsx | 56 ++++++++----------- .../module-permissions-dialog.test.js | 26 ++++++++- .../components/people-search-dialog.jsx | 23 +++++++- 3 files changed, 69 insertions(+), 36 deletions(-) diff --git a/packages/app/obojobo-repository/shared/components/module-permissions-dialog.jsx b/packages/app/obojobo-repository/shared/components/module-permissions-dialog.jsx index 0843d50a5f..20570cf996 100644 --- a/packages/app/obojobo-repository/shared/components/module-permissions-dialog.jsx +++ b/packages/app/obojobo-repository/shared/components/module-permissions-dialog.jsx @@ -44,40 +44,30 @@ class ModulePermissionsDialog extends React.Component { renderModal() { if (this.state.peoplePickerOpen) { - if (this.props.draftPermissions[this.props.draftId] != null) { - return ( - - - - ) - } else { - return ( - - - - ) + let draftPermissions = null + + if ( + this.props.draftPermissions[this.props.draftId] !== null && + this.props.draftPermissions[this.props.draftId] !== undefined + ) { + draftPermissions = this.props.draftPermissions[this.props.draftId].items } + return ( + + + + ) } return null } diff --git a/packages/app/obojobo-repository/shared/components/module-permissions-dialog.test.js b/packages/app/obojobo-repository/shared/components/module-permissions-dialog.test.js index 3784061463..870994ca6d 100644 --- a/packages/app/obojobo-repository/shared/components/module-permissions-dialog.test.js +++ b/packages/app/obojobo-repository/shared/components/module-permissions-dialog.test.js @@ -26,7 +26,7 @@ describe('ModulePermissionsDialog', () => { draftId: 'mockDraftId', title: 'Mock Module Title', currentUserId: 99, - draftPermissions: { items: [{ id: 99 }] }, + draftPermissions: {}, loadUsersForModule: jest.fn(), addUserToModule: jest.fn(), deleteModulePermissions: jest.fn(), @@ -49,6 +49,10 @@ describe('ModulePermissionsDialog', () => { expect(component.root.findAllByType(PeopleSearchDialog).length).toBe(isRendered ? 1 : 0) } + const expectModulePermissionsModalToBeRendered = (component, isRendered) => { + expect(component.root.findAllByType(ReactModal).length).toBe(isRendered ? 1 : 0) + } + test('renders with "null" draftPermissions', () => { let component act(() => { @@ -112,6 +116,26 @@ describe('ModulePermissionsDialog', () => { expectPeopleSearchModalToBeRendered(component, true) }) + test('clicking the "Add People" button opens the search dialog and passes it draftPermissions', () => { + defaultProps.draftPermissions['mockDraftId'] = { + items: [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 99 }] + } + const reusableComponent = + let component + act(() => { + component = create(reusableComponent) + }) + + expectModulePermissionsModalToBeRendered(component, false) + + act(() => { + component.root.findByProps({ id: 'modulePermissionsDialog-addPeopleButton' }).props.onClick() + component.update(reusableComponent) + }) + + expectModulePermissionsModalToBeRendered(component, true) + }) + test('modal closes the people search modal when callback is called', () => { const reusableComponent = let component diff --git a/packages/app/obojobo-repository/shared/components/people-search-dialog.jsx b/packages/app/obojobo-repository/shared/components/people-search-dialog.jsx index fe22c96523..2d95564c77 100644 --- a/packages/app/obojobo-repository/shared/components/people-search-dialog.jsx +++ b/packages/app/obojobo-repository/shared/components/people-search-dialog.jsx @@ -18,6 +18,24 @@ const PeopleSearchDialog = props => { props.onClose() } + const renderListItem = p => ( + + + + ) + + const renderValidListItems = p => { + if (props.draftPermissions) { + if (props.draftPermissions.every(d => d.id !== p.id)) { + return renderListItem(p) + } + } else { + return renderListItem(p) + } + } + return (
      @@ -35,7 +53,7 @@ const PeopleSearchDialog = props => {
        - {props.people.map(function(p) { + {/* {props.people.map(function(p) { if (props.draftPermissions) { if (props.draftPermissions.every(d => d.id !== p.id)) { return ( @@ -55,7 +73,8 @@ const PeopleSearchDialog = props => { ) } - })} + })} */} + {props.people.map(renderValidListItems)}
      From 10192dcfafd5344da7850af1acb95ea660923d9d Mon Sep 17 00:00:00 2001 From: Viggie1 Date: Thu, 27 Oct 2022 16:06:58 -0400 Subject: [PATCH 03/47] Implemented tooltip in editor for numeric input --- .../editor-component.test.js.snap | 784 ++++++++++++++++++ .../__snapshots__/numeric-option.test.js.snap | 112 +++ .../NumericAnswer/numeric-option.js | 10 + 3 files changed, 906 insertions(+) diff --git a/packages/obonode/obojobo-chunks-numeric-assessment/NumericAnswer/__snapshots__/editor-component.test.js.snap b/packages/obonode/obojobo-chunks-numeric-assessment/NumericAnswer/__snapshots__/editor-component.test.js.snap index c83cf8e84e..12c1b14500 100644 --- a/packages/obonode/obojobo-chunks-numeric-assessment/NumericAnswer/__snapshots__/editor-component.test.js.snap +++ b/packages/obonode/obojobo-chunks-numeric-assessment/NumericAnswer/__snapshots__/editor-component.test.js.snap @@ -12,6 +12,34 @@ exports[`NumericAnswer Editor Node NumericAnswer (requirement="exact",type="null className="select requirement" > Answer Type +
      +
      +
      + +
      +
      +
      Answer Type +
      +
      +
      + +
      +
      +
      Answer Type +
      +
      +
      + +
      +
      +
      Answer Type +
      +
      +
      + +
      +
      +
      Answer Type +
      +
      +
      + +
      +
      +
      Answer Type +
      +
      +
      + +
      +
      +
      Answer Type +
      +
      +
      + +
      +
      +
      Answer Type +
      +
      +
      + +
      +
      +
      Answer Type +
      +
      +
      + +
      +
      +
      Answer Type +
      +
      +
      + +
      +
      +
      Answer Type +
      +
      +
      + +
      +
      +
      Answer Type +
      +
      +
      + +
      +
      +
      Answer Type +
      +
      +
      + +
      +
      +
      Answer Type +
      +
      +
      + +
      +
      +
      Answer Type +
      +
      +
      + +
      +
      +
      Answer Type +
      +
      +
      + +
      +
      +
    +
    + ) +} + +export default EdgeControls diff --git a/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.js b/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.js new file mode 100644 index 0000000000..806bf15ba2 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.js @@ -0,0 +1,117 @@ +// import '../../viewer-component.scss' + +import React from 'react' +import { ReactEditor } from 'slate-react' +import { Editor, Transforms, Range } from 'slate' +import withSlateWrapper from 'obojobo-document-engine/src/scripts/oboeditor/components/node/with-slate-wrapper' +import EdgeControls from '../edge-controls' +import ExcerptEditControls from '../excerpt-edit-controls' +import getPresetProps from '../../get-preset-props' + +const getEdgeOptionsForBodyStyle = bodyStyle => { + switch (bodyStyle) { + case 'none': + return [] + + case 'filled-box': + case 'bordered-box': + case 'card': + case 'white-paper': + case 'modern-paper': + case 'light-yellow-paper': + case 'dark-yellow-paper': + case 'aged-paper': + return ['normal', 'fade', 'jagged'] + + default: + return ['normal', 'fade'] + } +} + +const ExcerptContent = props => { + const onChangeEdge = (edge, edgeType) => { + const [parent, parentPath] = Editor.parent( + props.editor, + ReactEditor.findPath(props.editor, props.element) + ) + const parentContent = parent.content + const newContent = { ...parentContent, [edge]: edgeType } + + // We update both this node and the parent node, as they both maintain copies of the same + // content - necessary to get both to re-render + Transforms.setNodes(props.editor, { content: { ...newContent } }, { at: parentPath }) + Transforms.setNodes( + props.editor, + { content: { ...newContent } }, + { at: ReactEditor.findPath(props.editor, props.element) } + ) + } + + // I was moving these methods over here, and then maybe I can no longer have to duplicate + // properties and just use properties on this one, Excerpt Content?! + const onChangePreset = presetValue => { + const [parent, parentPath] = Editor.parent( + props.editor, + ReactEditor.findPath(props.editor, props.element) + ) + const newContent = { + ...props.element.content, + ...getPresetProps(presetValue), + preset: presetValue + } + + const path = ReactEditor.findPath(props.editor, props.element) + + // We copy props on ExcerptContent - this is done to force ExcerptContent to update + Transforms.setNodes(props.editor, { content: { ...newContent } }, { at: path }) + Transforms.setNodes(props.editor, { content: { ...newContent } }, { at: parentPath }) + } + + const onChangeContentValue = (contentValueName, value) => { + const [parent, parentPath] = Editor.parent( + props.editor, + ReactEditor.findPath(props.editor, props.element) + ) + const path = ReactEditor.findPath(props.editor, props.element) + + const newContent = { ...props.element.content, [contentValueName]: value } + + Transforms.setNodes(props.editor, { content: { ...newContent } }, { at: path }) + Transforms.setNodes(props.editor, { content: { ...newContent } }, { at: parentPath }) + } + + const edgeOptions = getEdgeOptionsForBodyStyle(props.element.content.bodyStyle) + + const shouldShowEdgeControls = props.selected && Range.isCollapsed(props.editor.selection) + + return ( +
    + {shouldShowEdgeControls ? ( + + onChangeContentValue('topEdge', edgeType)} + /> + onChangeContentValue('bottomEdge', edgeType)} + /> + onChangeContentValue(propName, propValue)} + onChangePreset={onChangePreset} + /> + + ) : null} + +
    {props.children}
    +
    +
    + ) +} + +export default withSlateWrapper(ExcerptContent) diff --git a/packages/obonode/obojobo-chunks-excerpt/components/excerpt-edit-controls.js b/packages/obonode/obojobo-chunks-excerpt/components/excerpt-edit-controls.js new file mode 100644 index 0000000000..1112adabd0 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/components/excerpt-edit-controls.js @@ -0,0 +1,297 @@ +import React, { useState } from 'react' + +import iconFontSizeSmall from '../icon-font-size-small.svg' +import iconFontSizeMedium from '../icon-font-size-medium.svg' +import iconFontSizeLarge from '../icon-font-size-large.svg' +import iconLineHeightCompact from '../icon-line-height-compact.svg' +import iconLineHeightModerate from '../icon-line-height-moderate.svg' +import iconLineHeightGenerous from '../icon-line-height-generous.svg' +import iconWidthLarge from '../icon-width-large.svg' +import iconWidthMedium from '../icon-width-medium.svg' +import iconWidthSmall from '../icon-width-small.svg' +import iconWidthTiny from '../icon-width-tiny.svg' +import Button from 'obojobo-document-engine/src/scripts/common/components/button' +import RadioIcons from './radio-icons' + +const NO_EFFECT_DESCRIPTION = '(No effect available)' + +const presets = [ + { + label: 'Minimal', + value: 'minimal' + }, + { + label: 'Excerpt', + value: 'excerpt' + }, + { + label: 'Simple Filled', + value: 'simple-filled' + }, + { + label: 'Simple Bordered', + value: 'simple-bordered' + }, + { + label: 'Card', + value: 'card' + }, + { + label: 'Fiction', + value: 'fiction' + }, + { + label: 'Non-Fiction', + value: 'non-fiction' + }, + { + label: 'Historical', + value: 'historical' + }, + { + label: 'Very Historical', + value: 'very-historical' + }, + { + label: 'White Paper', + value: 'white-paper' + }, + { + label: 'Inst. Manual', + value: 'instruction-manual' + }, + { + label: 'Typewritten', + value: 'typewritten' + }, + { + label: 'Receipt', + value: 'receipt' + }, + { + label: 'Site / Doc', + value: 'modern-text-file' + }, + { + label: 'Retro Text File', + value: 'retro-text-file' + }, + { + label: 'Command Line', + value: 'computer-modern' + }, + { + label: 'Hacker Green', + value: 'computer-hacker-green' + }, + { + label: 'Hacker Orange', + value: 'computer-hacker-orange' + } +] + +const isEffectAvailable = bodyStyle => { + return getEffectDescription(bodyStyle) !== NO_EFFECT_DESCRIPTION +} + +const getEffectDescription = bodyStyle => { + switch (bodyStyle) { + case 'white-paper': + case 'modern-paper': + case 'light-yellow-paper': + case 'dark-yellow-paper': + case 'aged-paper': + return 'Paper background' + + case 'term-white': + case 'term-orange': + case 'term-green': + return 'Screen / glow effects' + + case 'modern-text-file': + case 'retro-text-file': + return 'Drop shadow' + + default: + return NO_EFFECT_DESCRIPTION + } +} + +const ExcerptEditControls = ({ content, onChangeProp, onChangePreset }) => { + const [isShowingMoreOptions, setIsShowingMoreOptions] = useState(false) + + return ( +
    { + switch (event.target.tagName) { + case 'INPUT': + case 'SELECT': + return + + default: + event.preventDefault() + } + }} + > +
    +
    + {isShowingMoreOptions ? null : ( +
      + {presets.map(p => { + return ( +
    • + +
    • + ) + })} +
    + )} +
    + + {isShowingMoreOptions ? ( +
    +
    + + +
    + +
    + + +
    + +
    + + }, + { label: 'medium', icon: }, + { label: 'small', icon: }, + { label: 'tiny', icon: } + ]} + selectedOption={content.width} + onChangeOption={value => onChangeProp('width', value)} + /> +
    + +
    + + }, + { label: 'regular', icon: }, + { label: 'larger', icon: } + ]} + selectedOption={content.fontSize} + onChangeOption={value => onChangeProp('fontSize', value)} + /> +
    +
    + + }, + { label: 'moderate', icon: }, + { label: 'generous', icon: } + ]} + selectedOption={content.lineHeight} + onChangeOption={value => onChangeProp('lineHeight', value)} + /> +
    +
    + +
    +
    + ) : null} +
    + +
    + ) +} + +export default ExcerptEditControls diff --git a/packages/obonode/obojobo-chunks-excerpt/components/line/editor-component.js b/packages/obonode/obojobo-chunks-excerpt/components/line/editor-component.js new file mode 100644 index 0000000000..2d293ce2d5 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/components/line/editor-component.js @@ -0,0 +1,17 @@ +import '../../viewer-component.scss' + +import React from 'react' + +const Line = props => { + return ( + + {props.children} + + ) +} + +export default Line diff --git a/packages/obonode/obojobo-chunks-excerpt/components/radio-icons.js b/packages/obonode/obojobo-chunks-excerpt/components/radio-icons.js new file mode 100644 index 0000000000..0dc35a7548 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/components/radio-icons.js @@ -0,0 +1,46 @@ +import React from 'react' + +const RadioIcons = ({ name, options, selectedOption, ariaLabel, onChangeOption }) => { + const onMouseDown = (option, event) => { + event.preventDefault() + + onChangeOption(option) + } + + const onChange = event => { + event.preventDefault() + + onChangeOption(event.target.value) + } + + return ( +
    +
    + {options.map((o, i) => ( + + ))} +
    +
    + ) +} + +export default RadioIcons diff --git a/packages/obonode/obojobo-chunks-excerpt/converter.js b/packages/obonode/obojobo-chunks-excerpt/converter.js new file mode 100644 index 0000000000..06075cc49a --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/converter.js @@ -0,0 +1,137 @@ +import Common from 'obojobo-document-engine/src/scripts/common' +import withoutUndefined from 'obojobo-document-engine/src/scripts/common/util/without-undefined' +import TextUtil from 'obojobo-document-engine/src/scripts/oboeditor/util/text-util' + + +const EXCERPT_NODE = 'ObojoboDraft.Chunks.Excerpt' +// const EXCERPT_TEXT_NODE = 'ObojoboDraft.Chunks.Excerpt.ExcerptText' +// const EXCERPT_TEXT_LINE_NODE = 'ObojoboDraft.Chunks.Excerpt.ExcerptLine' +// const CITE_TEXT_NODE = 'ObojoboDraft.Chunks.Excerpt.CitationText' +const CITE_LINE_NODE = 'ObojoboDraft.Chunks.Excerpt.CitationLine' +/** + * Generates an Obojobo Excerpt Node from a Slate node. + * Copies the id, type, triggers, and condenses ExcerptLine children and their + * text children (including marks) into a single textGroup + * @param {Object} node A Slate Node + * @returns {Object} An Obojobo Excerpt node + */ +const slateToObo = node => { + + // console.log('node:', node) + + const excerptContent = node.children[0].children + const citationText = node.children[1].children + + // const excerptTextGroup = excerptText.map(line => { + // const textLine = { + // text: { value: '', styleList: [] }, + // data: { + // indent: line.content.indent, + // hangingIndent: line.content.hangingIndent, + // align: line.content.align + // } + // } + + // TextUtil.slateToOboText(line, textLine) + + // return textLine + // }) + + const citationTextGroup = citationText.map(line => { + // console.log('line be', line) + + const textLine = { + text: { value: '', styleList: [] }, + data: { + indent: line.content.indent, + hangingIndent: line.content.hangingIndent, + align: line.content.align + } + } + + TextUtil.slateToOboText(line, textLine) + + return textLine + }) + + // console.log('excerpt content: ', excerptContent) + + return { + id: node.id, + type: node.type, + children: excerptContent.map(child => + Common.Registry.getItemForType(child.type).slateToObo(child) + ), + content: withoutUndefined({ + triggers: node.content.triggers, + citation: citationTextGroup, + bodyStyle: node.content.bodyStyle, + topEdge: node.content.topEdge, + bottomEdge: node.content.bottomEdge, + width: node.content.width, + font: node.content.font, + lineHeight: node.content.lineHeight, + fontSize: node.content.fontSize, + effect: node.content.effect + }) + } +} + +/** + * Generates a Slate node from an Obojobo Excerpt node. + * Copies all attributes, and converts a textGroup into Slate Text children + * Each textItem in the textgroup becomes a separate ExcerptLine node in order + * to properly leverage the Slate Editor's capabilities + * @param {Object} node An Obojobo Excerpt node + * @returns {Object} A Slate node + */ +const oboToSlate = node => { + const slateNode = Object.assign({}, node) + + // console.log('slateNode children: ', slateNode.children) + + + slateNode.children = slateNode.children.map(child =>{ + + // console.log('child: ', child) + + const children = Common.Registry.getItemForType(child.type).oboToSlate(child) + + return({ + type: EXCERPT_NODE, + subtype: 'ObojoboDraft.Chunks.Excerpt.ExcerptContent', + content: { + ...slateNode.content + }, + + children: children ? [children] : [] + }) + }) + + slateNode.children.push({ + type: 'ObojoboDraft.Chunks.Excerpt', + subtype: 'ObojoboDraft.Chunks.Excerpt.CitationText', + content: { indent: 0, hangingIndent: 0 }, + children: node.content.citation.map(line => { + const indent = line.data ? line.data.indent : 0 + const hangingIndent = line.data ? line.data.hangingIndent : false + const align = line.data ? line.data.align : 'left' + + return { + type: EXCERPT_NODE, + subtype: CITE_LINE_NODE, + content: { indent, hangingIndent, align }, + children: TextUtil.parseMarkings(line) + } + }) + }) + + // delete slateNode.content.excerpt + // delete slateNode.content.citation + + // console.log('oboToSlate', node, slateNode) + + return slateNode +} + +export default { slateToObo, oboToSlate } diff --git a/packages/obonode/obojobo-chunks-excerpt/converter.test.js b/packages/obonode/obojobo-chunks-excerpt/converter.test.js new file mode 100644 index 0000000000..6b24eaca1a --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/converter.test.js @@ -0,0 +1,148 @@ +// import { Transforms } from 'slate' +import Converter from './converter' + +import Common from 'obojobo-document-engine/src/scripts/common' +jest.mock('obojobo-document-engine/src/scripts/oboeditor/util/text-util') + +// const CODE_NODE = 'ObojoboDraft.Chunks.Code' +// const CODE_LINE_NODE = 'ObojoboDraft.Chunks.Code.CodeLine' +// const HEADING_NODE = 'ObojoboDraft.Chunks.Heading' +const TEXT_NODE = 'ObojoboDraft.Chunks.Text' +// const TEXT_LINE_NODE = 'ObojoboDraft.Chunks.Text.TextLine' +// const LIST_NODE = 'ObojoboDraft.Chunks.List' +// const LIST_LINE_NODE = 'ObojoboDraft.Chunks.List.Line' +const EXCERPT_NODE = 'ObojoboDraft.Chunks.Excerpt' + +describe('Excerpt Converter', () => { + + const mockSlateToObo = jest.fn(() => ({ type: 'OboNode' })) + const mockOboToSlate = jest.fn(() => ({ type: 'SlateNode' })) + + const mockGetItemForType = jest.spyOn(Common.Registry, 'getItemForType').mockImplementation(() => ({ + slateToObo: mockSlateToObo, + oboToSlate: mockOboToSlate + })) + + beforeEach(() => { + jest.clearAllMocks() + }) + + + test('slateToObo converts a Slate node to an OboNode with content', () => { + + const slateNode = { + id: 'mockKey', + type: EXCERPT_NODE, + content: { }, + children: [ + { + id: 'mockKey', + type: 'mockType', + content: {}, + children: [{ text: 'Excerpt text', b: true, type: TEXT_NODE }] + }, + { + type: 'mockType', + content: { hangingIndent: 0, indent: 0 }, + children: [{ + type: 'mockType', + content: { hangingIndent: 0, indent: 0 }, + children: [ + { text: 'Citation Line' } + ] + }] + } + ] + } + + const oboNode = Converter.slateToObo(slateNode) + + expect(oboNode).toMatchSnapshot() + }) + + test('slateToObo converts a Slate node to an OboNode with triggers', () => { + const slateNode = { + id: 'mockKey', + type: EXCERPT_NODE, + content: { triggers: 'mock-triggers' }, + children: [ + { + id: 'mockKey', + type: 'mockType', + content: {}, + children: [{ text: 'Excerpt text', b: true, type: TEXT_NODE }] + }, + { + type: 'mockType', + content: { hangingIndent: 0, indent: 0 }, + children: [{ + type: 'mockType', + content: { hangingIndent: 0, indent: 0 }, + children: [ + { text: 'Citation Line' } + ] + }] + } + ] + } + const oboNode = Converter.slateToObo(slateNode) + + // console.log(oboNode) + + // expect(false).toEqual(true) + + expect(oboNode).toMatchSnapshot() + + expect(mockGetItemForType).toHaveBeenCalled() + expect(mockGetItemForType).toHaveBeenCalledWith(TEXT_NODE) + }) + + test('oboToSlate converts an OboNode to a Slate node', () => { + + const nodeChild = { type: 'OboNode' } + + const oboNode = { + id: 'mockKey', + type: EXCERPT_NODE, + children: [ + nodeChild + ], + content: { + font: 'monospace', + bodyStyle: 'term-green', + citation: [ + { } + ] + } + } + const slateNode = Converter.oboToSlate(oboNode) + + expect(slateNode).toMatchSnapshot() + + expect(mockOboToSlate).toHaveBeenCalledWith(nodeChild) + }) + + test('oboToSlate converts an OboNode to a Slate node with citation line', () => { + const oboNode = { + id: 'mockKey', + type: EXCERPT_NODE, + children: [ + { type: 'OboNode' } + ], + content: { + font: 'monospace', + bodyStyle: 'term-green', + citation: [ + { + data: { align: 'center', indent: 0, hangingIndent: 0 }, + text: { value: 'Citation Text', styleList: [] } + } + ] + } + } + const slateNode = Converter.oboToSlate(oboNode) + + expect(slateNode).toMatchSnapshot() + }) + +}) diff --git a/packages/obonode/obojobo-chunks-excerpt/editor-component.js b/packages/obonode/obojobo-chunks-excerpt/editor-component.js new file mode 100644 index 0000000000..79a63895a0 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/editor-component.js @@ -0,0 +1,115 @@ +import Node from 'obojobo-document-engine/src/scripts/oboeditor/components/node/editor-component' +import withSlateWrapper from 'obojobo-document-engine/src/scripts/oboeditor/components/node/with-slate-wrapper' +import React from 'react' +import { Transforms } from 'slate' +import { ReactEditor } from 'slate-react' +import './editor-component.scss' +import getPresetProps from './get-preset-props' +import './viewer-component.scss' + + + +class Excerpt extends React.Component { + constructor(props) { + super(props) + + this.onChangePreset = this.onChangePreset.bind(this) + this.onClickMoreOptions = this.onClickMoreOptions.bind(this) + + this.state = { + isShowingMoreOptions: false + } + } + + onClickMoreOptions() { + this.setState({ + isShowingMoreOptions: !this.state.isShowingMoreOptions + }) + } + + onChangePreset(presetValue) { + const newContent = { + ...this.props.element.content, + ...getPresetProps(presetValue), + preset: presetValue + } + + const path = ReactEditor.findPath(this.props.editor, this.props.element) + Transforms.setNodes( + this.props.editor, + { + content: { ...newContent } + }, + { at: path } + ) + + // We copy props on ExcerptContent - this is done to force ExcerptContent to update + Transforms.setNodes(this.props.editor, { content: { ...newContent } }, { at: path.concat(0) }) + } + + onChangeContentValue(contentValueName, value) { + const path = ReactEditor.findPath(this.props.editor, this.props.element) + + const newContent = { ...this.props.element.content, [contentValueName]: value } + Transforms.setNodes(this.props.editor, { content: { ...newContent } }, { at: path }) + + // We copy props on ExcerptContent - this is done to force ExcerptContent to update + Transforms.setNodes(this.props.editor, { content: { ...newContent } }, { at: path.concat(0) }) + } + + render() { + const content = this.props.element.content + + // console.log('@TODO: Make sure these css class names wont bleed') + // console.log('@TODO: when excerpt is the last item the menu obscures the (+) button') + // console.log('@TODO: over time multiple cite lines are created') + // console.log('@TODO: glow text leaks into the caption') + // console.log('@TODO: hitting enter too quickly results in a crash') + return ( + +
    + {/* + */} +
    + {this.props.children} +
    +
    +
    + ) + } +} + +export default withSlateWrapper(Excerpt) diff --git a/packages/obonode/obojobo-chunks-excerpt/editor-component.scss b/packages/obonode/obojobo-chunks-excerpt/editor-component.scss new file mode 100644 index 0000000000..14e66d37be --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/editor-component.scss @@ -0,0 +1,639 @@ +@import '~styles/includes'; + +.radio-icons { + // border-radius: $dimension-rounded-radius; + + $size: 1.2em; + + .options { + height: $size; + // filter: brightness(0); + // opacity: 0.1; + // opacity: 0; + + // &:hover { + // filter: brightness(1); + // // transition: opacity $duration-animation-default, filter $duration-animation-default; + // opacity: 1; + // } + } + + .options > label > input { + opacity: 0; + position: fixed; + width: 0; + } + + .options > label { + position: relative; + width: $size; + height: $size; + border-left: 1px solid $color-shadow; + display: inline-block; + border: 1px solid $color-shadow; + background-color: $color-bg; + + img { + // content: ' '; + // position: absolute; + // left: 0; + // top: 0; + width: $size; + height: $size; + pointer-events: none; + background-size: 100% 100%; + opacity: 0.4; + filter: grayscale(100%); + } + + &:first-child { + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; + } + + &:last-child { + border-top-right-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; + } + + &.is-not-selected { + cursor: pointer; + } + + &:hover.is-not-selected { + border: 1px solid lighten($color-obojobo-blue, 40%); + background-color: lighten($color-obojobo-blue, 60%); + // z-index: 2; + + img { + opacity: 0.5; + filter: grayscale(0%); + } + } + + &.is-selected { + // &:hover.is-selected { + background-color: lighten($color-obojobo-blue, 60%); + border: 1px solid $color-obojobo-blue; + z-index: 1; + + img { + opacity: 1; + filter: grayscale(0%); + } + } + + // &.is-option-small:after { + // background-image: url('./icon-font-size-large.svg'); + // } + // &.is-option-small:after { + // background-image: url('./icon-font-size-large.svg'); + // } + // &.is-option-small:after { + // background-image: url('./icon-font-size-large.svg'); + // } + } + + span { + width: 0; + overflow: hidden; + display: inline-block; + height: 0; + } + + img { + vertical-align: top; + } +} + +.obojobo-draft--chunks--excerpt--edge-controls { + z-index: 1; + + // border-radius: $dimension-rounded-radius; + display: inline-block; + position: absolute; + // left: 50%; + left: 0; + // transform: translate(-50%, 0); + // top: 0; + display: flex; + justify-content: center; + // width: 100%; + align-items: center; + height: 1em; + // opacity: 0; + // filter: grayscale(100%); + // background: blue; + // transition: opacity 1s; + border: 2px dotted transparentize(lighten($color-obojobo-blue, 40%), 0.5); + transform: translate(-50%, -50%); + left: 50%; + width: 100%; + + .wrapper { + background: red; + // content: ' '; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + } + + &:hover { + opacity: 1; + } + + &.is-position-top { + top: 0; + // transform: translate(-50%, -50%); + } + + &.is-position-bottom { + bottom: 0; + transform: translate(-50%, 50%); + + label:after { + transform: rotate(180deg); + } + } + + .edges { + height: 1em; + transform: translate(0, -2px); + // filter: brightness(0); + // opacity: 0.1; + // opacity: 0; + + // &:hover { + // filter: brightness(1); + // // transition: opacity $duration-animation-default, filter $duration-animation-default; + // opacity: 1; + // } + } + + input { + opacity: 0; + position: fixed; + width: 0; + } + + label { + position: relative; + width: 1em; + height: 1em; + // border-left: 2px solid $color-shadow-light; + display: inline-block; + // border: 2px solid $color-shadow-light; + border: 2px solid lighten($color-obojobo-blue, 40%); + background-color: $color-bg; + + &:after { + content: ' '; + position: absolute; + left: 0; + top: 0; + width: 1em; + height: 1em; + pointer-events: none; + background-size: 100% 100%; + opacity: 0.2; + // filter: grayscale(100%); + } + + // &:first-child { + // border-top-left-radius: $dimension-rounded-radius; + // border-bottom-left-radius: $dimension-rounded-radius; + // } + + &:nth-child(2) { + transform: translate(-2px); + } + + &:nth-child(3) { + transform: translate(-4px); + // border-top-right-radius: $dimension-rounded-radius; + // border-bottom-right-radius: $dimension-rounded-radius; + } + + &:hover.is-not-selected { + background-color: lighten($color-obojobo-blue, 60%); + // z-index: 2; + + &:after { + opacity: 0.5; + filter: grayscale(0%); + } + } + + &.is-selected { + // &:hover.is-selected { + background-color: lighten($color-obojobo-blue, 60%); + border: 2px solid $color-obojobo-blue; + z-index: 1; + + &:after { + opacity: 0.5; + filter: grayscale(0%); + } + } + + &.is-not-selected { + cursor: pointer; + } + + &.is-edge-normal:after { + // background-image: url('./icon-flat-edge.svg'); + width: 100%; + height: 50%; + border-bottom: 0.5em solid #1451a4; + } + + &.is-edge-fade:after { + background-image: url('./icon-fade-edge.png'); + } + + &.is-edge-jagged:after { + background-image: url('./icon-jagged-edge.png'); + } + } + + &:hover label { + // opacity: 1; + // filter: none; + } + + span { + width: 0; + overflow: hidden; + display: inline-block; + height: 0; + } +} + +.obojobo-draft--chunks--excerpt { + .radio-icons { + font-size: 2em; + display: inline-block; + + display: block; + /* margin: 0 auto; */ + text-align: center; + margin-bottom: 0.5em; + } + + .attributes-box { + position: absolute; + // text-align: center; + border-radius: $dimension-rounded-radius; + font-size: 0.75em; + // left: 50%; + // bottom: -6em; + // transform: translate(-50%, 0); + font-family: $font-default; + box-sizing: border-box; + min-height: 2.5em; + background-color: $color-bg2; + box-shadow: $box-shadow-overlay; + // z-index: 9; + + left: 0; + z-index: 9999; + width: 18em; + top: 0; + transform: translate(-100%, 0) translate(-1.5em, 0); + + &.is-open { + bottom: -26.5em; + width: 23em; + } + + @mixin point() { + // content: ' '; + // position: absolute; + // left: 50%; + // transform: translate(-50%, -50%) rotate(45deg); + // width: 1.72em; + // height: 1.72em; + // border-radius: $dimension-rounded-radius 0; + // z-index: -1; + + content: ' '; + position: absolute; + /* left: 50%; */ + transform: translate(50%, -50%) rotate(45deg); + width: 1.72em; + height: 1.72em; + border-radius: 0.25em 0; + z-index: -1; + background-color: #f4f4f4; + right: 0; + top: 4em; + } + + .box-border { + background-color: $color-bg2; + padding: 1em; + } + + &::before { + @include point(); + + background-color: $color-bg2; + + box-shadow: $box-shadow-overlay; + right: -0.6em; + top: 1em; + left: auto; + position: absolute; + transform: rotate(45deg); + content: ' '; + width: 1.25em; + height: 1.25em; + // background: $color-shadow; + background: $color-bg2; + box-shadow: $box-shadow-overlay; + } + + .attributes-list { + background-color: $color-bg; + border-radius: $dimension-rounded-radius; + + > div { + margin-bottom: 0.5em; + } + + .attribute { + display: inline-block; + padding: 0.5em; + } + + .attribute-label { + // display: block; + font-weight: bold; + text-align: left; + width: 6em; + display: inline-block; + + display: block; + width: 100%; + text-align: center; + } + + .effect-settings { + &.is-not-enabled { + opacity: 0.5; + } + + cursor: pointer; + text-align: left; + width: 100%; + display: flex; + /* display: flex; */ + align-items: center; + margin-bottom: 1em; + margin-top: 2em; + + span { + font-weight: bold; + // vertical-align: middle; + display: inline-block; + line-height: 1em; + transform: translate(0, 1px); + } + + input { + // vertical-align: middle; + margin: 0; + margin-right: 0.5em; + } + } + + // input { + // @include text-input(); + + // width: 22em; + // } + } + } + + .attributes-box { + // width: 35em; + // bottom: 0; + // transform: translate(-50%, 100%); + font-size: 0.8rem; + + // top: 1.4em; + + > .obojobo-draft--components--button { + margin-bottom: 1em; + margin-left: auto; + margin-right: auto; + width: 100%; + + button { + padding: 0.5em 1em; + } + } + } + + .obojobo-draft--chunks--excerpt--edge-controls { + opacity: 0; + + .edges { + opacity: 0; + } + } + + &.is-selected { + .obojobo-draft--chunks--excerpt--edge-controls { + opacity: 1; + + &:hover { + .edges { + opacity: 1; + } + } + } + } + + // &.is-width-large { + // .attributes-box { + // left: 3.1em; + // } + // } + // &.is-width-medium { + // .attributes-box { + // left: 7.4em; + // } + // } + // &.is-width-small { + // .attributes-box { + // left: 11em; + // } + // } + // &.is-width-tiny { + // .attributes-box { + // left: 14.8em; + // } + // } + + .attributes-list { + margin: 1em; + // padding: 1em; + } + + .preset-list { + list-style-type: none; + text-align: left; + margin: 0; + padding: 0; + display: grid; + grid-template-columns: 1fr 1fr; + height: 18em; + overflow: scroll; + // background: $color-bg2; + border-radius: $dimension-rounded-radius; + border-top: 1px solid $color-shadow-light; + + li { + padding: 0.25em 0; + + // &.is-selected { + // background: blue; + // } + + &:hover { + background: lighten($color-obojobo-blue, 60%); + } + } + + button { + border: none; + background: transparent; + font-size: 1em; + font-family: $font-default; + text-align: center; + // outline: 1px solid red; + width: 100%; + height: 100%; + cursor: pointer; + } + + .icon { + // background: $color-bg; + width: 4em; + height: 3em; + border-radius: $dimension-rounded-radius; + display: block; + margin: 0 auto; + background-image: url('./icons-presets.png'); + background-position: 0px 0px; + // background-size: cover; + background-size: 927px 39px; + background-repeat: no-repeat; + + @function pos-x($i) { + @return $i * -51.5px; + } + + &.icon-minimal { + background-position-x: pos-x(0); + } + + &.icon-simple-filled { + background-position-x: pos-x(1); + } + + &.icon-simple-bordered { + background-position-x: pos-x(2); + } + + &.icon-card { + background-position-x: pos-x(3); + } + + &.icon-fiction { + background-position-x: pos-x(4); + } + + &.icon-non-fiction { + background-position-x: pos-x(5); + } + + &.icon-historical { + background-position-x: pos-x(6); + } + + &.icon-very-historical { + background-position-x: pos-x(7); + } + + &.icon-white-paper { + background-position-x: pos-x(8); + } + + &.icon-instruction-manual { + background-position-x: pos-x(9); + } + + &.icon-typewritten { + background-position-x: pos-x(10); + } + + &.icon-receipt { + background-position-x: pos-x(11); + } + + &.icon-computer-modern { + background-position-x: pos-x(12); + } + + &.icon-computer-hacker-green { + background-position-x: pos-x(13); + } + + &.icon-modern-text-file { + background-position-x: pos-x(14); + } + + &.icon-retro-text-file { + background-position-x: pos-x(15); + } + + &.icon-computer-hacker-orange { + background-position-x: pos-x(16); + } + + &.icon-excerpt { + background-position-x: pos-x(17); + } + } + + span { + display: inline-block; + font-size: 0.8em; + } + } + + // &.is-showing-more-options { + // .preset-list { + // border-bottom-left-radius: 0; + // border-bottom-right-radius: 0; + // border-bottom: 1px solid $color-shadow; + // } + // } + + .more-options { + padding: 1em; + padding-top: 0; + } + + select { + @include select-input(); + + font-size: 0.9em; + margin-bottom: 0.5em; + width: 100%; + } +} diff --git a/packages/obonode/obojobo-chunks-excerpt/editor-registration.js b/packages/obonode/obojobo-chunks-excerpt/editor-registration.js new file mode 100644 index 0000000000..74d0612b5e --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/editor-registration.js @@ -0,0 +1,314 @@ +import { Editor, Node, Element, Transforms, Text } from 'slate' +import { ReactEditor } from 'slate-react' +import Converter from './converter' +import Icon from './icon' +import KeyDownUtil from 'obojobo-document-engine/src/scripts/oboeditor/util/keydown-util' +import Line from './components/line/editor-component' +import ExcerptContent from './components/excerpt-content/editor-component' +import Citation from './components/citation/editor-component' +import EditorComponent from './editor-component' +import React from 'react' +import emptyNode from './empty-node.json' + +import normalizeNode from './changes/normalize-node' +import decreaseIndent from './changes/decrease-indent' +import increaseIndent from './changes/increase-indent' +import indentOrTab from './changes/indent-or-tab' + +const EXCERPT_NODE = 'ObojoboDraft.Chunks.Excerpt' +const EXCERPT_CONTENT = 'ObojoboDraft.Chunks.Excerpt.ExcerptContent' +const CITE_TEXT_NODE = 'ObojoboDraft.Chunks.Excerpt.CitationText' +const CITE_LINE_NODE = 'ObojoboDraft.Chunks.Excerpt.CitationLine' +const EXCERPT_TEXT_NODE = 'ObojoboDraft.Chunks.Excerpt.ExcerptText' +const EXCERPT_TEXT_LINE_NODE = 'ObojoboDraft.Chunks.Excerpt.ExcerptLine' +const TEXT_NODE = 'ObojoboDraft.Chunks.Text' +const TEXT_LINE_NODE = 'ObojoboDraft.Chunks.Text.TextLine' + +// { +/* + + + + + + + + +{ + type: "Excerpt", + content: { + body: [ {} ], + citation: [ {} ] + } +} */ +// } + +const Excerpt = { + name: EXCERPT_NODE, + icon: Icon, + menuLabel: 'Box / Excerpt', + isInsertable: true, + isContent: true, + helpers: Converter, + json: { + emptyNode + }, + plugins: { + // Editor Plugins - These get attached to the editor object and override it's default functions + // They may affect multiple nodes simultaneously + insertData(data, editor, next) { + // Insert Slate fragments normally + if (data.types.includes('application/x-slate-fragment')) return next(data) + + // If the node that we will be inserting into is not a Excerpt node use the regular logic + const [first] = Editor.nodes(editor, { match: node => Element.isElement(node) }) + if (first[0].type !== EXCERPT_NODE) return next(data) + + // When inserting plain text into a Excerpt node insert all lines as excerpt + const plainText = data.getData('text/plain') + const fragment = plainText.split('\n').map(text => ({ + type: EXCERPT_NODE, + subtype: EXCERPT_TEXT_LINE_NODE, + content: { indent: 0, hangingIndent: false }, + children: [{ text }] + })) + + Transforms.insertFragment(editor, fragment) + }, + // normalizeNode, + // Editable Plugins - These are used by the PageEditor component to augment React functions + // They affect individual nodes independently of one another + decorate([node, path], editor) { + // Define a placeholder decoration + + const placeholders = [] + + if ( + Element.isElement(node) && + node.subtype === EXCERPT_TEXT_LINE_NODE && + Node.string(node) === '' && + path[path.length - 1] === 0 + ) { + const point = Editor.start(editor, path) + + placeholders.push({ + placeholder: 'Type your excerpt here', + anchor: point, + focus: point + }) + } + + if ( + Element.isElement(node) && + node.subtype === CITE_LINE_NODE && + Node.string(node) === '' && + path[path.length - 1] === 0 + ) { + const point = Editor.start(editor, path) + + placeholders.push({ + placeholder: 'Type your optional citation here', + anchor: point, + focus: point + }) + } + + return placeholders + }, + onKeyDown(entry, editor, event) { + switch (event.key) { + //@TODO: + // case 'Backspace': + // case 'Delete': + // return KeyDownUtil.deleteEmptyParent(event, editor, entry, event.key === 'Delete') + + case 'Tab': + // TAB+SHIFT + if (event.shiftKey) return decreaseIndent(entry, editor, event) + + // TAB+ALT + if (event.altKey) return increaseIndent(entry, editor, event) + + // TAB + return indentOrTab(entry, editor, event) + + case 'Enter': + return KeyDownUtil.breakToText(event, editor, entry) + } + }, + normalizeNode(entry, editor, next) { + // console.log('in normalize') + const [node, path] = entry + + // console.log('normalizing', node) + + // console.log('Children: ', Node.children(editor, path)) + + // if (Element.isElement(node) && node.type === EXCERPT_NODE && !node.subtype) { + // console.log('NN', node, path) + // } + + // EXCERPT_CONTENT shouldn't have any loose text nodes: + if ( + Element.isElement(node) && + node.type === EXCERPT_NODE && + node.subtype === EXCERPT_CONTENT + ) { + // Wrap any loose children into TextLines (Text normalization should then turn them + // into complete Text chunks) + for (const [child, childPath] of Node.children(editor, path)) { + if (Text.isText(child)) { + return Transforms.wrapNodes( + editor, + { + type: TEXT_NODE, + subtype: TEXT_LINE_NODE, + content: { indent: 0 } + }, + { at: childPath } + ) + } + } + } + + // CITE_TEXT_NODE must contain one and only one CITE_LINE_NODE + if ( + Element.isElement(node) && + node.type === EXCERPT_NODE && + node.subtype === CITE_TEXT_NODE + ) { + let citeLineNode = null + const nodesToRemove = [] + + for (const [child, childPath] of Node.children(editor, path)) { + switch (child.subtype) { + case CITE_LINE_NODE: + if (!citeLineNode) { + citeLineNode = child + } else { + nodesToRemove.push(childPath) + } + + break + + default: + nodesToRemove.push(childPath) + } + } + + // console.log('nodesToRemove:', nodesToRemove.length) + + if (nodesToRemove.length > 0) { + // console.log('REMOVE') + + for (const node of nodesToRemove) { + Transforms.removeNodes(editor, { at: node }) + } + + return + } + + if (!citeLineNode) { + // console.log('ADD', 'CitationLine') + Transforms.insertNodes( + editor, + [ + { + type: EXCERPT_NODE, + subtype: CITE_LINE_NODE, + content: { indent: 0, hangingIndent: 0, align: 'center' }, + children: [{ text: '' }] + } + ], + { at: path } + ) + return + } + } + + // There must be one and only one EXCERPT_CONTENT and CITE_TEXT_NODE, in that order, + // and no other types of children + if (Element.isElement(node) && node.type === EXCERPT_NODE && !node.subtype) { + const nodesToRemove = [] + let contentNode = null + let citeTextNode = null + + for (const [child, childPath] of Node.children(editor, path)) { + // console.log('child: ', child) + switch (child.subtype) { + case EXCERPT_CONTENT: + if (!contentNode) { + contentNode = child + } else { + nodesToRemove.push(childPath) + } + break + + case CITE_TEXT_NODE: + if (!citeTextNode && contentNode) { + citeTextNode = child + } else { + nodesToRemove.push(childPath) + } + break + + default: + nodesToRemove.push(childPath) + break + } + } + + // console.log('nodesToRemove: ', nodesToRemove.length) + + if (nodesToRemove.length > 0) { + + // Remove Node + for (const node of nodesToRemove) { + Transforms.removeNodes(editor, { at: node }) + } + } + + if (!contentNode) { + // Add in content nodes + const a = { ...emptyNode.children[0] } + // console.log('ADD2', a) + Transforms.insertNodes(editor, a, { at: path.concat(0) }) + return + } + + if (!citeTextNode) { + const b = { ...emptyNode.children[1] } + // console.log('ADD3', path, b, path.concat(1)) + Transforms.insertNodes(editor, b, { at: path.concat(1) }) + return + } + } + + // console.log('exit normalize') + next(entry, editor) + }, + renderNode(props) { + // console.log('in render with ', props) + switch (props.element.subtype) { + case CITE_LINE_NODE: + return + + case CITE_TEXT_NODE: + return + + case EXCERPT_CONTENT: + return + + default: + return + } + } + } +} + +// const EXCERPT_NODE = 'ObojoboDraft.Chunks.Excerpt' + +// const CITE_TEXT_NODE = 'ObojoboDraft.Chunks.Excerpt.CitationText' +// const CITE_LINE_NODE = 'ObojoboDraft.Chunks.Excerpt.CitationLine' + +export default Excerpt diff --git a/packages/obonode/obojobo-chunks-excerpt/editor-registration.test.js b/packages/obonode/obojobo-chunks-excerpt/editor-registration.test.js new file mode 100644 index 0000000000..c3665faab0 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/editor-registration.test.js @@ -0,0 +1,458 @@ +jest.mock('slate') +jest.mock('slate-react') +jest.mock('obojobo-document-engine/src/scripts/oboeditor/util/keydown-util') +jest.mock('./changes/increase-indent') +jest.mock('./changes/decrease-indent') +jest.mock('./changes/indent-or-tab') + +import { Editor, Element, Node, Text, Transforms } from 'slate' +import decreaseIndent from './changes/decrease-indent' +import increaseIndent from './changes/increase-indent' +import indentOrTab from './changes/indent-or-tab' +import emptyNode from './empty-node.json' +import Excerpt from './editor-registration' +import KeyDownUtil from 'obojobo-document-engine/src/scripts/oboeditor/util/keydown-util' + + +const EXCERPT_NODE = 'ObojoboDraft.Chunks.Excerpt' +const EXCERPT_TEXT_LINE_NODE = 'ObojoboDraft.Chunks.Excerpt.ExcerptLine' +const EXCERPT_CONTENT = 'ObojoboDraft.Chunks.Excerpt.ExcerptContent' +const CITE_TEXT_NODE = 'ObojoboDraft.Chunks.Excerpt.CitationText' +const CITE_LINE_NODE = 'ObojoboDraft.Chunks.Excerpt.CitationLine' + +describe('Excerpt editor', () => { + + describe('insertData', () => { + test('calls next if pasting a Slate fragment', () => { + const data = { + types: ['application/x-slate-fragment'] + } + const next = jest.fn() + + Excerpt.plugins.insertData(data, {}, next) + + expect(next).toHaveBeenCalled() + }) + + test('calls next if not pasting into Code', () => { + const data = { + types: ['application/html'] + } + const next = jest.fn() + Editor.nodes.mockImplementation((editor, { match }) => { + match({ type: 'nonExcerptNode' }) + return [[{ type: 'nonExcerptNode' }]] + }) + + Excerpt.plugins.insertData(data, {}, next) + + expect(next).toHaveBeenCalled() + }) + + test('inserts all lines as ExcerptTextLines if pasting into Code', () => { + const data = { + types: ['application/html'], + getData: () => 'line1 \n line2' + } + const next = jest.fn() + Editor.nodes.mockReturnValueOnce([[{ type: EXCERPT_NODE }]]) + + Excerpt.plugins.insertData(data, {}, next) + + expect(Transforms.insertFragment).toHaveBeenCalledWith({}, [ + { + type: EXCERPT_NODE, + subtype: EXCERPT_TEXT_LINE_NODE, + content: { indent: 0, hangingIndent: false }, + children: [{ text: 'line1 ' }] + }, + { + type: EXCERPT_NODE, + subtype: EXCERPT_TEXT_LINE_NODE, + content: { indent: 0, hangingIndent: false }, + children: [{ text: ' line2' }] + } + ]) + }) + }) + + describe('renderNode', () => { + test('renders excerpt when passed', () => { + const props = { + attributes: { dummy: 'dummyData' }, + element: { + type: EXCERPT_NODE, + content: {} + } + } + + expect(Excerpt.plugins.renderNode(props)).toMatchSnapshot() + }) + + test('renders excerpt content when passed', () => { + const props = { + attributes: { dummy: 'dummyData' }, + element: { + type: EXCERPT_NODE, + subtype: EXCERPT_CONTENT, + content: {} + } + } + + expect(Excerpt.plugins.renderNode(props)).toMatchSnapshot() + }) + + test('renders excerpt citation line when passed', () => { + const props = { + attributes: { dummy: 'dummyData' }, + element: { + type: EXCERPT_NODE, + subtype: CITE_LINE_NODE, + content: {} + } + } + + expect(Excerpt.plugins.renderNode(props)).toMatchSnapshot() + }) + + test('renders excerpt citation text when passed', () => { + const props = { + attributes: { dummy: 'dummyData' }, + element: { + type: EXCERPT_NODE, + subtype: CITE_TEXT_NODE, + content: {} + } + } + + expect(Excerpt.plugins.renderNode(props)).toMatchSnapshot() + }) + }) + + describe('decorate', () => { + test('renders no placeholders when not relevent', () => { + const placeholders = Excerpt.plugins.decorate([{ text: 'mock text' }], {}) + + expect(placeholders).toEqual([]) + }) + + test('renders a placeholder for excerpt', () => { + const editor = { + children: [{ children: [{ text: '' }] }] + } + + const node = { + subtype: EXCERPT_TEXT_LINE_NODE, + children: [{ text: '' }] + } + + const point = { key: 'pointKey' } + + Element.isElement.mockReturnValue(true) + Node.string.mockReturnValue('') + Editor.start.mockReturnValue(point) + + const actualPlaceholders = Excerpt.plugins.decorate([node, [0]], editor) + + expect(actualPlaceholders).toEqual([ + { + placeholder: 'Type your excerpt here', + anchor: point, + focus: point + } + ]) + }) + + test('renders a placeholder for excerpt', () => { + const editor = { + children: [{ children: [{ text: '' }] }] + } + + const node = { + subtype: CITE_LINE_NODE, + children: [{ text: '' }] + } + + const point = { key: 'pointKey' } + + Element.isElement.mockReturnValue(true) + Node.string.mockReturnValue('') + Editor.start.mockReturnValue(point) + + const actualPlaceholders = Excerpt.plugins.decorate([node, [0]], editor) + + expect(actualPlaceholders).toEqual([ + { + placeholder: 'Type your optional citation here', + anchor: point, + focus: point + } + ]) + }) + }) + + describe('onKeyDown', () => { + test('deals with [Shift]+[Tab]', () => { + const event = { + key: 'Tab', + shiftKey: true, + preventDefault: jest.fn() + } + + Excerpt.plugins.onKeyDown([{}, [0]], {}, event) + + expect(decreaseIndent).toHaveBeenCalled() + }) + + test('deals with [Alt]+[Tab]', () => { + const event = { + key: 'Tab', + altKey: true, + preventDefault: jest.fn() + } + + Excerpt.plugins.onKeyDown([{}, [0]], {}, event) + + expect(increaseIndent).toHaveBeenCalled() + }) + + test('deals with [Tab]', () => { + const event = { + key: 'Tab', + preventDefault: jest.fn() + } + + Excerpt.plugins.onKeyDown([{}, [0]], {}, event) + + expect(indentOrTab).toHaveBeenCalled() + }) + + test('deals with [Enter]', () => { + const event = { + key: 'Enter', + preventDefault: jest.fn() + } + + Excerpt.plugins.onKeyDown([{}, [0]], {}, event) + + expect(KeyDownUtil.breakToText).toHaveBeenCalled() + + }) + }) + + describe('normalizeNode', () => { + const editor = { type: 'mockEditor' } + + const wrapNodesSpy = Transforms.wrapNodes.mockReturnValue() + const removeNodesSpy = Transforms.removeNodes.mockReturnValue() + const insertNodesSpy = Transforms.insertNodes.mockReturnValue() + + Element.isElement.mockReturnValue(true) + + afterEach(() => { + jest.clearAllMocks(); + }) + + test('wraps loose text nodes in excerpt content', () => { + + const childPath = [0] + const child = { type: 'child node' } + + Node.children.mockReturnValue([ + [child, childPath] + ]) + + Text.isText.mockReturnValue(true) + + Excerpt.plugins.normalizeNode([{ + type: EXCERPT_NODE, + subtype: EXCERPT_CONTENT + }, [0]], editor, jest.fn()) + + expect(wrapNodesSpy).toHaveBeenCalled() + expect(wrapNodesSpy).toHaveBeenCalledWith( + editor, + { + type: 'ObojoboDraft.Chunks.Text', + subtype: 'ObojoboDraft.Chunks.Text.TextLine', + content: { indent: 0 } + }, + { at: childPath } + ) + }) + + test('does not wrap loose node if not a text node', () => { + const childPath = [0] + const child = { type: 'child node' } + + Node.children.mockReturnValue([ + [child, childPath] + ]) + + Text.isText.mockReturnValue(false) + + Excerpt.plugins.normalizeNode([{ + type: EXCERPT_NODE, + subtype: EXCERPT_CONTENT + }, [0]], editor, jest.fn()) + + expect(wrapNodesSpy).not.toHaveBeenCalled() + }) + + test('removes extra citation line nodes', () => { + + const childPaths = [[0], [1], [2]] + const children = [ + { subtype: CITE_LINE_NODE }, + { subtype: CITE_LINE_NODE }, + { subtype: '' }, + ] + + Node.children.mockReturnValue([ + [children[0], childPaths[0]], + [children[1], childPaths[1]], + [children[2], childPaths[2]], + ]) + + Excerpt.plugins.normalizeNode([{ + type: EXCERPT_NODE, + subtype: CITE_TEXT_NODE + }, [0]], editor, jest.fn()) + + expect(removeNodesSpy).toHaveBeenCalledTimes(2) + + expect(removeNodesSpy).not.toHaveBeenCalledWith(editor, { at: childPaths[0]} ) + expect(removeNodesSpy).toHaveBeenCalledWith(editor, { at: childPaths[1]} ) + expect(removeNodesSpy).toHaveBeenCalledWith(editor, { at: childPaths[2]} ) + + }) + + test('does not remove citation line node if there is only one', () => { + const childPaths = [[0]] + const children = [ + { subtype: CITE_LINE_NODE }, + ] + + Node.children.mockReturnValue([ + [children[0], childPaths[0]], + ]) + + Excerpt.plugins.normalizeNode([{ + type: EXCERPT_NODE, + subtype: CITE_TEXT_NODE + }, [0]], editor, jest.fn()) + + expect(removeNodesSpy).not.toHaveBeenCalled() + }) + + test('adds an empty citation line node if there are not any', () => { + + Node.children.mockReturnValue([]) + + Excerpt.plugins.normalizeNode([{ + type: EXCERPT_NODE, + subtype: CITE_TEXT_NODE + }, [0]], editor, jest.fn()) + + expect(insertNodesSpy).toHaveBeenCalled() + + expect(insertNodesSpy).toHaveBeenCalledWith( + editor, + [{ + type: EXCERPT_NODE, + subtype: CITE_LINE_NODE, + content: { indent: 0, hangingIndent: 0, align: 'center' }, + children: [{ text: '' }] + }], + { at: [0]} + ) + }) + + test('removes children if more than 1 excerpt content or cite text node', () => { + + const excerptChildren = [ + [{ subtype: EXCERPT_CONTENT }, [0]], + [{ subtype: EXCERPT_CONTENT }, [1]] + ] + + const citationTextChildren = [ + [{ subtype: CITE_TEXT_NODE }, [2]], + [{ subtype: CITE_TEXT_NODE }, [3]] + ] + + Node.children.mockReturnValue([ + excerptChildren[0], + excerptChildren[1], + citationTextChildren[0], + citationTextChildren[1], + [{ subtype: 'OtherType' }, [4]] + ]) + + Excerpt.plugins.normalizeNode([{ + type: EXCERPT_NODE, + }, [0]], editor, jest.fn()) + + expect(removeNodesSpy).toHaveBeenCalledTimes(3) + }) + + test('adds content node if one does not exist', () => { + Node.children.mockReturnValue([]) + + const path = [0] + + Excerpt.plugins.normalizeNode([{ + type: EXCERPT_NODE, + }, path], editor, jest.fn()) + + expect(removeNodesSpy).toHaveBeenCalledTimes(0) + + expect(insertNodesSpy).toHaveBeenCalledTimes(1) + expect(insertNodesSpy).toHaveBeenCalledWith( + editor, + { ...emptyNode.children[0] }, + { at: path.concat(0) } + ) + }) + + test('adds citation text node if one does not exist', () => { + Node.children.mockReturnValue([ + [{ subtype: EXCERPT_CONTENT }, [3]] + ]) + + const path = [0] + + Excerpt.plugins.normalizeNode([{ + type: EXCERPT_NODE, + }, path], editor, jest.fn()) + + expect(removeNodesSpy).toHaveBeenCalledTimes(0) + + expect(insertNodesSpy).toHaveBeenCalledTimes(1) + expect(insertNodesSpy).toHaveBeenCalledWith( + editor, + { ...emptyNode.children[1] }, + { at: path.concat(1) } + ) + }) + + test('makes no changes if not necessary', () => { + const excerptChild = [{ subtype: EXCERPT_CONTENT }, [0]] + + const citationTextChild = [{ subtype: CITE_TEXT_NODE }, [2]] + + Node.children.mockReturnValue([ + excerptChild, + citationTextChild, + ]) + + Excerpt.plugins.normalizeNode([{ + type: EXCERPT_NODE, + }, [0]], editor, jest.fn()) + + expect(insertNodesSpy).not.toHaveBeenCalled() + expect(removeNodesSpy).not.toHaveBeenCalled() + expect(wrapNodesSpy).not.toHaveBeenCalled() + }) + }) + +}) diff --git a/packages/obonode/obojobo-chunks-excerpt/editor.js b/packages/obonode/obojobo-chunks-excerpt/editor.js new file mode 100644 index 0000000000..a4998dcd38 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/editor.js @@ -0,0 +1,7 @@ +// Main entrypoint for editor +import Common from 'obojobo-document-engine/src/scripts/common' +import EditorNode from './editor-registration' + +console.log('registering editor model for excerpt') +// register +Common.Registry.registerEditorModel(EditorNode) diff --git a/packages/obonode/obojobo-chunks-excerpt/empty-node.json b/packages/obonode/obojobo-chunks-excerpt/empty-node.json new file mode 100644 index 0000000000..8ad700bdc0 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/empty-node.json @@ -0,0 +1,47 @@ +{ + "type": "ObojoboDraft.Chunks.Excerpt", + "content": { + "bodyStyle": "filled-box", + "width": "medium", + "font": "sans", + "lineHeight": "moderate", + "fontSize": "smaller", + "topEdge": "normal", + "bottomEdge": "normal", + "effect": false + }, + "children": [ + { + "type": "ObojoboDraft.Chunks.Excerpt", + "subtype": "ObojoboDraft.Chunks.Excerpt.ExcerptContent", + "content": {}, + "children": [ + { + "type": "ObojoboDraft.Chunks.Text", + "content": {}, + "children": [ + { + "type": "ObojoboDraft.Chunks.Text", + "subtype": "ObojoboDraft.Chunks.Text.TextLine", + "content": { "indent": 0 }, + "children": [{ "text": "" }] + } + ] + } + ] + }, + { + "type": "ObojoboDraft.Chunks.Excerpt", + "subtype": "ObojoboDraft.Chunks.Excerpt.CitationText", + "content": { "indent": 0, "hangingIndent": 0 }, + "children": [ + { + "type": "ObojoboDraft.Chunks.Excerpt", + "subtype": "ObojoboDraft.Chunks.Excerpt.CitationLine", + "content": { "indent": 0, "hangingIndent": 0, "align": "center" }, + "children": [{ "text": "" }] + } + ] + } + ] +} diff --git a/packages/obonode/obojobo-chunks-excerpt/get-preset-props.js b/packages/obonode/obojobo-chunks-excerpt/get-preset-props.js new file mode 100644 index 0000000000..17c527b4fd --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/get-preset-props.js @@ -0,0 +1,265 @@ +const getPresetProps = presetName => { + switch (presetName) { + case 'minimal': { + return { + bodyStyle: 'none', + width: 'medium', + font: 'serif', + lineHeight: 'moderate', + fontSize: 'smaller', + topEdge: 'normal', + bottomEdge: 'normal', + effect: false + } + } + + case 'excerpt': { + return { + bodyStyle: 'none', + width: 'medium', + font: 'palatino', + lineHeight: 'moderate', + fontSize: 'smaller', + topEdge: 'normal', + bottomEdge: 'normal', + effect: false + } + } + + case 'simple-filled': { + return { + bodyStyle: 'filled-box', + width: 'medium', + font: 'sans', + lineHeight: 'moderate', + fontSize: 'smaller', + topEdge: 'normal', + bottomEdge: 'normal', + effect: false + } + } + + case 'simple-bordered': { + return { + bodyStyle: 'bordered-box', + width: 'medium', + font: 'sans', + lineHeight: 'moderate', + fontSize: 'smaller', + topEdge: 'normal', + bottomEdge: 'normal', + effect: false + } + } + + case 'card': { + return { + bodyStyle: 'card', + width: 'medium', + font: 'sans', + lineHeight: 'moderate', + fontSize: 'smaller', + topEdge: 'normal', + bottomEdge: 'normal', + effect: false + } + } + + case 'fiction': { + return { + bodyStyle: 'light-yellow-paper', + width: 'medium', + font: 'palatino', + lineHeight: 'generous', + fontSize: 'smaller', + topEdge: 'normal', + bottomEdge: 'normal', + effect: true + } + } + + case 'non-fiction': { + return { + bodyStyle: 'modern-paper', + width: 'medium', + font: 'georgia', + lineHeight: 'generous', + fontSize: 'smaller', + topEdge: 'normal', + bottomEdge: 'normal', + effect: true + } + } + + case 'historical': { + return { + bodyStyle: 'dark-yellow-paper', + width: 'medium', + font: 'palatino', + lineHeight: 'generous', + fontSize: 'smaller', + topEdge: 'normal', + bottomEdge: 'normal', + effect: true + } + } + + case 'very-historical': { + return { + bodyStyle: 'aged-paper', + width: 'medium', + font: 'palatino', + lineHeight: 'generous', + fontSize: 'smaller', + topEdge: 'normal', + bottomEdge: 'normal', + effect: true + } + } + + case 'white-paper': { + return { + bodyStyle: 'white-paper', + width: 'medium', + font: 'times-new-roman', + lineHeight: 'moderate', + fontSize: 'regular', + topEdge: 'normal', + bottomEdge: 'fade', + effect: false + } + } + + case 'instruction-manual': { + return { + bodyStyle: 'modern-paper', + width: 'medium', + font: 'helvetica', + lineHeight: 'moderate', + fontSize: 'smaller', + topEdge: 'normal', + bottomEdge: 'fade', + effect: false + } + } + + case 'typewritten': { + return { + bodyStyle: 'white-paper', + width: 'medium', + font: 'courier', + lineHeight: 'moderate', + fontSize: 'smaller', + topEdge: 'normal', + bottomEdge: 'jagged', + effect: false + } + } + + case 'receipt': { + return { + bodyStyle: 'white-paper', + width: 'tiny', + font: 'monospace', + lineHeight: 'moderate', + fontSize: 'smaller', + topEdge: 'normal', + bottomEdge: 'jagged', + effect: false + } + } + + case 'computer-modern': { + return { + bodyStyle: 'command-line', + width: 'medium', + font: 'monospace', + lineHeight: 'moderate', + fontSize: 'smaller', + topEdge: 'normal', + bottomEdge: 'normal', + effect: false + } + } + + case 'computer-hacker-white': { + return { + bodyStyle: 'term-white', + width: 'medium', + font: 'monospace', + lineHeight: 'moderate', + fontSize: 'smaller', + topEdge: 'normal', + bottomEdge: 'normal', + effect: true + } + } + + case 'computer-hacker-green': { + return { + bodyStyle: 'term-green', + width: 'medium', + font: 'monospace', + lineHeight: 'moderate', + fontSize: 'smaller', + topEdge: 'normal', + bottomEdge: 'normal', + effect: true + } + } + + case 'computer-hacker-orange': { + return { + bodyStyle: 'term-orange', + width: 'medium', + font: 'monospace', + lineHeight: 'moderate', + fontSize: 'smaller', + topEdge: 'normal', + bottomEdge: 'normal', + effect: true + } + } + + case 'modern-text-file': { + return { + bodyStyle: 'modern-text-file', + width: 'medium', + font: 'helvetica', + lineHeight: 'generous', + fontSize: 'smaller', + topEdge: 'normal', + bottomEdge: 'normal', + effect: true + } + } + + case 'retro-text-file': { + return { + bodyStyle: 'retro-text-file', + width: 'medium', + font: 'monospace', + lineHeight: 'generous', + fontSize: 'smaller', + topEdge: 'normal', + bottomEdge: 'normal', + effect: true + } + } + + case 'computer-c64': { + return { + bodyStyle: 'term-c64', + width: 'medium', + font: 'monospace', + lineHeight: 'moderate', + fontSize: 'regular', + topEdge: 'normal', + bottomEdge: 'normal', + effect: false + } + } + } +} + +export default getPresetProps diff --git a/packages/obonode/obojobo-chunks-excerpt/icon-fade-edge.png b/packages/obonode/obojobo-chunks-excerpt/icon-fade-edge.png new file mode 100644 index 0000000000000000000000000000000000000000..2cd421ea4e07bfc22a902ac1b456b21c2c53b9f9 GIT binary patch literal 629 zcmV-*0*d{KP)HGo@zm@w}RRJm-5okb=d#zh46(>nU43B&dd)0!7J zp70XHAJ11}kK}}R^hYJZsk*>uLe<0CZ@_@MEQxy)TdD-{iVk) zB&EvK=4mPs5^rWrrNxq(f5khhm=)l~-C6;_Jdt5=)o0jS_GQC$MY`^fnY{v#Q;paG zNDDrujA5JVm~8!i!2TH5h#4r~99s(zG?Pl6%9ezR>N0pBj)~30F0iSBCFmukXoj&R zas(C34%GAk{B3WJZ#|ZUvxgtoP}GUJdaU^^!^9G-S&Z==dxpc<@Bx6h9PE1#CFb&A zUm@WF=jX&Lu?Nb!(Aee}oNp^Oy(eY)=CayFJUQNo6)FY4Y2MwIG*D@{gheFDi7Vp8 zh@C{1>h`gUErr&q);$~H0f8 zEQ4b#cjioa)Gf)fnwNp$L%aI#ziRVhn5EXWY(<4>ZK{?ou}pIMM_2m+&0_F$^>bP0 Hl+XkKl(H$U literal 0 HcmV?d00001 diff --git a/packages/obonode/obojobo-chunks-excerpt/icon-flat-edge.svg b/packages/obonode/obojobo-chunks-excerpt/icon-flat-edge.svg new file mode 100644 index 0000000000..a2c6967ed4 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/icon-flat-edge.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/icon-font-size-large.svg b/packages/obonode/obojobo-chunks-excerpt/icon-font-size-large.svg new file mode 100644 index 0000000000..509be8a116 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/icon-font-size-large.svg @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/icon-font-size-medium.svg b/packages/obonode/obojobo-chunks-excerpt/icon-font-size-medium.svg new file mode 100644 index 0000000000..0c0eb707db --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/icon-font-size-medium.svg @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/icon-font-size-small.svg b/packages/obonode/obojobo-chunks-excerpt/icon-font-size-small.svg new file mode 100644 index 0000000000..06177ebc75 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/icon-font-size-small.svg @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/icon-jagged-edge.png b/packages/obonode/obojobo-chunks-excerpt/icon-jagged-edge.png new file mode 100644 index 0000000000000000000000000000000000000000..c3c016937f55bd164ccb9668a941d0e64571cfe5 GIT binary patch literal 325 zcmeAS@N?(olHy`uVBq!ia0vp^Y9P$P1|(P5zFfz^z{un2;usRq`u56xufq;92R^P3 ze>q|07v5bRvmd-ji&-5YqP5SlAycWaywo&@x5%4yI$7dZ^w6QR;~wfKZIq<_-vTAALviGt6K2z+O)b41?*X@J&l3| z(>pe=$X8(AcPCNP!aARm=kDhRh07RIcQcCRIclAnW*u{Fqn^vdyq7z79c2((Tyy \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/icon-line-height-generous.svg b/packages/obonode/obojobo-chunks-excerpt/icon-line-height-generous.svg new file mode 100644 index 0000000000..25c3f0f0fe --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/icon-line-height-generous.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/icon-line-height-moderate.svg b/packages/obonode/obojobo-chunks-excerpt/icon-line-height-moderate.svg new file mode 100644 index 0000000000..9b59088fc9 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/icon-line-height-moderate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/icon-width-large.svg b/packages/obonode/obojobo-chunks-excerpt/icon-width-large.svg new file mode 100644 index 0000000000..3078f96b35 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/icon-width-large.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/icon-width-medium.svg b/packages/obonode/obojobo-chunks-excerpt/icon-width-medium.svg new file mode 100644 index 0000000000..9b064acc4b --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/icon-width-medium.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/icon-width-small.svg b/packages/obonode/obojobo-chunks-excerpt/icon-width-small.svg new file mode 100644 index 0000000000..357a958337 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/icon-width-small.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/icon-width-tiny.svg b/packages/obonode/obojobo-chunks-excerpt/icon-width-tiny.svg new file mode 100644 index 0000000000..8f8304cd9b --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/icon-width-tiny.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/icon.js b/packages/obonode/obojobo-chunks-excerpt/icon.js new file mode 100644 index 0000000000..2e4647a050 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/icon.js @@ -0,0 +1,44 @@ +import React from 'react' + +const Icon = () => { + return ( + + + + + + + + + + + + + + + + + + + + + ) +} + +export default Icon diff --git a/packages/obonode/obojobo-chunks-excerpt/icons-presets.png b/packages/obonode/obojobo-chunks-excerpt/icons-presets.png new file mode 100644 index 0000000000000000000000000000000000000000..f9a0bddd89ed7edbaeb708b68f435c110767c647 GIT binary patch literal 12798 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 O%1S9oR($;O?SB9QI2#}U literal 0 HcmV?d00001 diff --git a/packages/obonode/obojobo-chunks-excerpt/index.js b/packages/obonode/obojobo-chunks-excerpt/index.js new file mode 100644 index 0000000000..93ce1af802 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/index.js @@ -0,0 +1,8 @@ +module.exports = { + obojobo: { + clientScripts: { + viewer: 'viewer.js', + editor: 'editor.js' + } + } +} diff --git a/packages/obonode/obojobo-chunks-excerpt/jagged-edge-aged-paper.svg b/packages/obonode/obojobo-chunks-excerpt/jagged-edge-aged-paper.svg new file mode 100644 index 0000000000..1ca164ac55 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/jagged-edge-aged-paper.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/jagged-edge-bordered-box.svg b/packages/obonode/obojobo-chunks-excerpt/jagged-edge-bordered-box.svg new file mode 100644 index 0000000000..f899c53bf9 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/jagged-edge-bordered-box.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/jagged-edge-dark-yellow-paper.svg b/packages/obonode/obojobo-chunks-excerpt/jagged-edge-dark-yellow-paper.svg new file mode 100644 index 0000000000..1e62d03db0 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/jagged-edge-dark-yellow-paper.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/jagged-edge-filled-box.svg b/packages/obonode/obojobo-chunks-excerpt/jagged-edge-filled-box.svg new file mode 100644 index 0000000000..1b7f0c298a --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/jagged-edge-filled-box.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/jagged-edge-light-yellow-paper.svg b/packages/obonode/obojobo-chunks-excerpt/jagged-edge-light-yellow-paper.svg new file mode 100644 index 0000000000..afc1388e34 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/jagged-edge-light-yellow-paper.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/jagged-edge-modern-paper.svg b/packages/obonode/obojobo-chunks-excerpt/jagged-edge-modern-paper.svg new file mode 100644 index 0000000000..1932c2ad88 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/jagged-edge-modern-paper.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/jagged-edge-white-paper.svg b/packages/obonode/obojobo-chunks-excerpt/jagged-edge-white-paper.svg new file mode 100644 index 0000000000..f37cd17ebc --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/jagged-edge-white-paper.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/jagged-edge.svg b/packages/obonode/obojobo-chunks-excerpt/jagged-edge.svg new file mode 100644 index 0000000000..1e62d03db0 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/jagged-edge.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/package.json b/packages/obonode/obojobo-chunks-excerpt/package.json new file mode 100644 index 0000000000..e7d7ab19d4 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/package.json @@ -0,0 +1,60 @@ +{ + "name": "obojobo-chunks-excerpt", + "version": "9.1.1", + "license": "AGPL-3.0-only", + "description": "Excerpt content chunk for Obojobo", + "scripts": { + "test": "TZ='America/New_York' jest --verbose", + "test:ci": "TZ='America/New_York' CI=true jest --ci --useStderr --coverage --coverageReporters text-summary cobertura", + "lint": "yarn lint:js && yarn lint:css", + "lint:js": "eslint .", + "lint:css": "stylelint **/*.scss", + "prettier:run": "prettier --write '**/*.{js,scss}'", + "precommit": "lint-staged" + }, + "lint-staged": { + "**/*.scss": [ + "stylelint" + ], + "**/*.{js,scss}": [ + "prettier --write" + ] + }, + "prettier": { + "printWidth": 100, + "semi": false, + "useTabs": true, + "singleQuote": true + }, + "peerDependencies": { + "obojobo-lib-utils": "^15.0.0" + }, + "jest": { + "testMatch": [ + "**/*.test.js" + ], + "setupFilesAfterEnv": [ + "obojobo-lib-utils/test-setup-chunks.js" + ], + "verbose": false, + "coverageReporters": [ + "text", + "lcov" + ], + "coveragePathIgnorePatterns": [ + "node_modules" + ], + "moduleNameMapper": { + "^Common(.*)$": "obojobo-document-engine/src/scripts/common$1", + "\\.(svg|scss)$": "obojobo-lib-utils/__mocks__/ignore-file-stub.js" + }, + "coverageThreshold": { + "global": { + "branches": 100, + "functions": 100, + "lines": 100, + "statements": 100 + } + } + } +} diff --git a/packages/obonode/obojobo-chunks-excerpt/paper.jpg b/packages/obonode/obojobo-chunks-excerpt/paper.jpg new file mode 100644 index 0000000000000000000000000000000000000000..732953919192a2e87145aabd51c24e1483c5555e GIT binary patch literal 17760 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 literal 0 HcmV?d00001 diff --git a/packages/obonode/obojobo-chunks-excerpt/paper.png b/packages/obonode/obojobo-chunks-excerpt/paper.png new file mode 100644 index 0000000000000000000000000000000000000000..22ad6825cdda5b4e0bf722f7357c63947c3d49d9 GIT binary patch literal 16789 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 literal 0 HcmV?d00001 diff --git a/packages/obonode/obojobo-chunks-excerpt/prettier.config.js b/packages/obonode/obojobo-chunks-excerpt/prettier.config.js new file mode 100644 index 0000000000..1c4e9398bc --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/prettier.config.js @@ -0,0 +1,2 @@ +const defaults = require('obojobo-lib-utils/prettier.config') +module.exports = defaults diff --git a/packages/obonode/obojobo-chunks-excerpt/retro-document-top.svg b/packages/obonode/obojobo-chunks-excerpt/retro-document-top.svg new file mode 100644 index 0000000000..dca575d951 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/retro-document-top.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/tab.svg b/packages/obonode/obojobo-chunks-excerpt/tab.svg new file mode 100644 index 0000000000..8433ec7899 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/tab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/obonode/obojobo-chunks-excerpt/test.jpg b/packages/obonode/obojobo-chunks-excerpt/test.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a1139a0b545aaef87ddaa65253857c6a22faed9f GIT binary patch literal 5864 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 INvL@Je*~WA8UO$Q literal 0 HcmV?d00001 diff --git a/packages/obonode/obojobo-chunks-excerpt/viewer-component.js b/packages/obonode/obojobo-chunks-excerpt/viewer-component.js new file mode 100644 index 0000000000..678d82f407 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/viewer-component.js @@ -0,0 +1,59 @@ +import './viewer-component.scss' + +import Common from 'obojobo-document-engine/src/scripts/common' +import React from 'react' +import Viewer from 'obojobo-document-engine/src/scripts/viewer' + +const { OboComponent } = Viewer.components +const { TextGroupEl } = Common.chunk.textChunk +const { TextChunk } = Common.chunk + +const Excerpt = props => { + const modelState = props.model.modelState + + console.log(modelState) + + return ( + + +
    +
    +
    + {props.model.children.models.map((child, index) => { + const Component = child.getComponentClass() + + return + })} +
    +
    +
    + + {!modelState.citation.isBlank ? ( + + {modelState.citation.items.map((textItem, index) => ( + + ))} + + ) : null} +
    +
    +
    + ) +} + +export default Excerpt diff --git a/packages/obonode/obojobo-chunks-excerpt/viewer-component.scss b/packages/obonode/obojobo-chunks-excerpt/viewer-component.scss new file mode 100644 index 0000000000..5f7a61559e --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/viewer-component.scss @@ -0,0 +1,901 @@ +@import '~styles/includes'; + +// .overlay { +// background: url('./test.jpg'); + +// // border-radius: $dimension-rounded-radius * 3; +// width: 100%; +// position: absolute; +// left: 0; +// top: 0; +// right: 0; +// bottom: 0; +// } + +.obojobo-draft--chunks--excerpt, +.component[data-obo-component='true'] .obojobo-draft--chunks--excerpt { + position: relative; + + &.is-font-serif .component[data-obo-component='true'] { + font-family: $font-text; + } + + &.is-font-sans .component[data-obo-component='true'] { + font-family: $font-default; + } + + &.is-font-monospace .component[data-obo-component='true'] { + font-family: $font-monospace; + + > .text-chunk > h1, + > .text-chunk > h2, + > .text-chunk > h3, + > .text-chunk > h4, + > .text-chunk > h5, + > .text-chunk > h6 { + font-family: $font-monospace; + } + } + + &.is-font-times-new-roman .component[data-obo-component='true'] { + font-family: 'Times New Roman', Times, serif; + font-size: 0.9em; + + > .text-chunk > h1, + > .text-chunk > h2, + > .text-chunk > h3, + > .text-chunk > h4, + > .text-chunk > h5, + > .text-chunk > h6 { + font-family: 'Times New Roman', Times, serif; + } + } + + &.is-font-georgia .component[data-obo-component='true'] { + font-family: Georgia, 'Times New Roman', Times, serif; + + > .text-chunk > h1, + > .text-chunk > h2, + > .text-chunk > h3, + > .text-chunk > h4, + > .text-chunk > h5, + > .text-chunk > h6 { + font-family: Georgia, 'Times New Roman', Times, serif; + } + } + + &.is-font-helvetica .component[data-obo-component='true'] { + font-family: Helvetica, Arial, sans-serif; + + > .text-chunk > h1, + > .text-chunk > h2, + > .text-chunk > h3, + > .text-chunk > h4, + > .text-chunk > h5, + > .text-chunk > h6 { + font-family: Helvetica, Arial, sans-serif; + } + } + + &.is-font-courier .component[data-obo-component='true'] { + font-family: 'Courier', monospace; + + > .text-chunk > h1, + > .text-chunk > h2, + > .text-chunk > h3, + > .text-chunk > h4, + > .text-chunk > h5, + > .text-chunk > h6 { + font-family: 'Courier', monospace; + } + } + + &.is-font-palatino .component[data-obo-component='true'] { + font-family: 'Palatino', Georgia, 'Times New Roman', Times, serif; + + > .text-chunk > h1, + > .text-chunk > h2, + > .text-chunk > h3, + > .text-chunk > h4, + > .text-chunk > h5, + > .text-chunk > h6 { + font-family: 'Palatino', Georgia, 'Times New Roman', Times, serif; + } + } + + &.is-line-height-type-compact .obo-text { + line-height: 1.5em; + } + + &.is-line-height-type-moderate .obo-text { + line-height: 1.7em; + } + + &.is-line-height-type-generous .obo-text { + line-height: 2em; + } + + &.is-font-size-smaller { + .excerpt-content > .wrapper > .component[data-obo-component='true'] { + font-size: 0.85em; + } + + .component-toolbar { + font-size: 1.1764705882em; + } + } + + &.is-font-size-larger { + .excerpt-content > .wrapper > .component[data-obo-component='true'] { + font-size: 1.15em; + } + + .component-toolbar { + font-size: 0.8695652174em; + } + } + + blockquote { + margin: 0; + text-align: center; + + > cite { + margin-top: 0.5em; //1em; + display: inline-block; + // width: 100%; + font-style: normal; + font-family: $font-default; + color: $color-text-minor; + font-size: 0.7em; + position: relative; + text-align: center; + + // background: red; + } + } + + .excerpt-content { + margin: 0 auto; + padding: 1em 0; + position: relative; + text-align: left; + + > .overlay { + pointer-events: none; + } + + > .component[data-obo-component='true'] { + padding-left: 0; + padding-right: 0; + } + + > .wrapper { + position: relative; + } + } + + &.is-width-tiny { + .excerpt-content { + max-width: 20em; + } + + cite { + width: 100%; + max-width: 28.57em; + } + } + + &.is-width-small { + .excerpt-content { + max-width: 25em; + } + + cite { + width: 100%; + max-width: 35.71em; + } + } + + &.is-width-medium { + .excerpt-content { + max-width: 31em; + } + + cite { + width: 100%; + max-width: 44.29em; + } + } + + &.is-width-large { + .excerpt-content, + cite { + margin-left: 0; + margin-right: 0; + } + + cite { + width: 100%; + } + } + + &.is-body-style-type-filled-box { + .excerpt-content { + border-radius: $dimension-rounded-radius; + background: #f9f9f9; + color: #333333; + border-radius: 0.25em; + // line-height: 1.8em; + } + } + + &.is-body-style-type-bordered-box { + .excerpt-content { + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: $dimension-rounded-radius; + border-radius: 0.25em; + // line-height: 1.8em; + } + } + + &.is-body-style-type-card { + .excerpt-content { + box-shadow: 0 5px 5px rgba(0, 0, 0, 0.1); + + background: #f9f9f9; + + .obo-text, + .katex { + color: #333333; + } + } + } + + &.is-body-style-type-command-line { + .excerpt-content { + border-radius: $dimension-rounded-radius; + border-radius: 0.25em; + background: #1b1916; + // line-height: 1.8em; + + .obo-text, + .katex { + color: #d8d8d8; + } + } + } + + &.is-body-style-type-retro-text-file { + .excerpt-content { + border: 1px solid black; + + background: #d6d6d6; + padding-top: 1.5em; + padding-left: 0.2em; + padding-right: 0.2em; + padding-bottom: 0.2em; + // line-height: 1.8em; + + .wrapper { + background: white; + border: 1px solid black; + padding-top: 0.5em; + padding-bottom: 1em; + // border: 2px solid #a7a7a7; + } + + .pad { + padding-left: 2em; + padding-right: 2em; + } + + // .obo-text, .katex { + // color: #d8d8d8; + // } + } + + .wrapper:before { + content: ' '; + width: 100%; + height: 16px; + position: absolute; + left: 0; + top: -1.2em; + + background: url(./retro-document-top.svg); + } + + &.is-showing-effect { + .excerpt-content { + box-shadow: 2px 2px black; + } + } + } + + &.is-body-style-type-none { + blockquote { + margin-top: 0; + margin-bottom: 0; + } + + .excerpt-content { + padding-top: 0; + padding-bottom: 0; + } + } + + &.is-body-style-type-modern-text-file { + .excerpt-content { + border-radius: 0.25em; + + background: #f4f4f4; + border: 2px solid #ccc; + padding-bottom: 0; + } + + .wrapper { + margin-top: 0.5em; + background: white; + border-bottom-left-radius: 0.25em; + border-bottom-right-radius: 0.25em; + border-top: 2px solid #ccc; + padding-top: 1em; + padding-bottom: 1em; + } + + .wrapper:before { + content: ' '; + + position: absolute; + // left: 0; + // top: -1.2em; + + width: 130px; + height: 28px; + position: absolute; + left: 2em; + top: 0; + transform: translate(0, -100%); + + background: url(./tab.svg); + background-size: cover; + } + + &.is-showing-effect { + .excerpt-content { + box-shadow: 0px 10px 9px rgba(0, 0, 0, 0.2); + } + } + } + + &.is-body-style-type-term-white { + .excerpt-content { + border-radius: $dimension-rounded-radius * 3; + background: linear-gradient(#383635, #1b1916); + // line-height: 1.8em; + + .obo-text, + .katex { + color: white; + } + } + + &.is-showing-effect { + .obo-text, + .katex { + text-shadow: 1px 1px 11px rgba(255, 255, 255, 0.3), 1px 1px 11px rgba(255, 255, 255, 0.3); + } + + .overlay { + // content: ' '; + border-radius: $dimension-rounded-radius * 3; + width: 100%; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + background: repeating-linear-gradient( + rgba(0, 0, 0, 0), + rgba(0, 0, 0, 0) 1px, + #1b1916 1px, + #1b1916 2px + ); + opacity: 0.2; + // pointer-events: none; + } + } + } + + &.is-body-style-type-term-green { + .excerpt-content { + border-radius: $dimension-rounded-radius * 3; + background: linear-gradient(#383635, #1b1916); + // line-height: 1.8em; + + .obo-text, + .katex { + color: #83ff27; + } + } + + &.is-showing-effect { + .obo-text, + .katex { + text-shadow: 1px 1px 11px rgba(131, 255, 39, 0.3), 1px 1px 11px rgba(131, 255, 39, 0.3); + } + + .overlay { + // content: ' '; + border-radius: $dimension-rounded-radius * 3; + width: 100%; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + background: repeating-linear-gradient( + rgba(0, 0, 0, 0), + rgba(0, 0, 0, 0) 1px, + #1b1916 1px, + #1b1916 2px + ); + opacity: 0.2; + // pointer-events: none; + } + } + } + + &.is-body-style-type-term-orange { + .excerpt-content { + border-radius: $dimension-rounded-radius * 3; + background: linear-gradient(#383635, #1b1916); + // line-height: 1.8em; + + .obo-text, + .katex { + color: #ffaa27; + } + } + + &.is-showing-effect { + .obo-text, + .katex { + text-shadow: 1px 1px 11px rgba(255, 170, 39, 0.3), 1px 1px 11px rgba(255, 170, 39, 0.3); + } + + .overlay { + // content: ' '; + border-radius: $dimension-rounded-radius * 3; + width: 100%; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + background: repeating-linear-gradient( + rgba(0, 0, 0, 0), + rgba(0, 0, 0, 0) 1px, + #1b1916 1px, + #1b1916 2px + ); + opacity: 0.2; + // pointer-events: none; + } + } + } + + &.is-body-style-type-term-c64 { + .excerpt-content { + // border-radius: $dimension-rounded-radius * 3; + background: #181663; + border: 1em solid #a6a2ef; + // line-height: 1.8em; + + .obo-text, + .katex { + color: #a6a2ef; + } + } + } + + &.is-body-style-type-light-yellow-paper { + .excerpt-content { + // line-height: 2em; + background: #fffcf8; + + // color: #443b35; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); + border: 1px solid rgba(0, 0, 0, 0.03); + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + + .obo-text, + .katex { + // line-height: 2em; + color: #443b35; + } + } + + &.is-showing-effect { + .excerpt-content { + background-image: url('./test.jpg'); + background-blend-mode: luminosity; + } + } + } + + &.is-body-style-type-dark-yellow-paper { + .excerpt-content { + background: #fdf5ea; // repeating-linear-gradient(0deg, #e1e1ff, #e1e1ff 1px, transparent 1px, transparent 20px); + // background-attachment: fixed; + border-radius: 0.12em; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); + /* border: 1px solid rgba(0, 0, 0, 0.03); */ + border-bottom: 1px solid rgb(220, 217, 212); + + .obo-text, + .katex { + // line-height: 2em; + color: #565655; + } + } + + &.is-showing-effect { + .excerpt-content { + background-image: url('./test.jpg'); + background-blend-mode: luminosity; + } + } + } + + &.is-body-style-type-aged-paper { + .excerpt-content { + // line-height: 2em; + background: #fffcf8; + // color: #443b35; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); + border: 1px solid rgba(0, 0, 0, 0.03); + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + background: rgb(237, 220, 192); + background: radial-gradient(circle, rgba(237, 220, 192, 1) 50%, rgba(214, 187, 158, 1) 100%); + // color: #423120; + + background: radial-gradient(circle, #eddcc0 50%, #d6bb9e 100%); + /* background-image: url(/static/04f943f…-413.jpg); */ + // background-blend-mode: multiply; + + .obo-text, + .katex { + // line-height: 2em; + color: #423120; + } + } + + &.is-showing-effect { + .excerpt-content { + background: radial-gradient(circle, #eddcc0 50%, #d6bb9e 100%), url('./test.jpg'); + background-blend-mode: multiply; + } + } + } + + &.is-body-style-type-white-paper { + .excerpt-content { + // line-height: 2em; + background: #fbfbfd; + // color: #; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); + border: 1px solid rgba(0, 0, 0, 0.03); + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + + .obo-text, + .katex { + // line-height: 2em; + color: #242425; + } + } + + &.is-showing-effect { + .excerpt-content { + background-image: url('./test.jpg'); + background-blend-mode: luminosity; + } + } + } + + &.is-body-style-type-modern-paper { + .excerpt-content { + // line-height: 2em; + background: #fbfbfd; + background: #f9f8f7; + // color: #242425; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); + border: 1px solid rgba(0, 0, 0, 0.03); + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + + .obo-text, + .katex { + // line-height: 2em; + color: #242425; + } + } + + &.is-showing-effect { + .excerpt-content { + background-image: url('./test.jpg'); + background-blend-mode: luminosity; + } + } + } + + &.is-top-edge-type-fade { + .excerpt-content:before { + content: ' '; + width: calc(100% + 6px); + transform: translate(-3px, -3px); + position: absolute; + left: 0; + top: 0; + height: 30px; + background: linear-gradient(white, rgba(0, 0, 0, 0)); + + background: rgb(255, 255, 255); + background: linear-gradient(180deg, rgba(255, 255, 255, 1) 10%, rgba(255, 255, 255, 0) 100%); + } + + &.is-body-style-type-modern-text-file { + .excerpt-content { + padding-top: 0; + } + + .wrapper { + margin-top: 0; + border-top: none; + padding-top: 0; + + &:before { + display: none; + } + } + } + + &.is-body-style-type-retro-text-file { + .excerpt-content { + padding-top: 0; + } + + .wrapper { + margin-top: 0; + border-top: none; + padding-top: 0; + + &:before { + display: none; + } + } + } + + &.is-body-style-type-command-line, + &.is-body-style-type-term-white, + &.is-body-style-type-term-green, + &.is-body-style-type-term-orange { + .excerpt-content:before { + background: linear-gradient(180deg, white 50%, rgba(255, 255, 255, 0) 100%); + } + + .overlay:before { + content: ' '; + width: calc(100% + 6px); + transform: translate(-3px, -3px); + position: absolute; + left: 0; + top: 0; + height: 30px; + background: linear-gradient( + 180deg, + rgba(255, 255, 255, 1) 50%, + rgba(255, 255, 255, 0) 100% + ); + } + } + + &.is-body-style-type-term-c64 { + .excerpt-content:before { + width: calc(100% + 50px); + transform: translate(-25px, -20px); + height: 45px; + background: linear-gradient(180deg, white 50%, rgba(255, 255, 255, 0) 100%); + } + } + } + + &.is-bottom-edge-type-fade { + .excerpt-content:after { + content: ' '; + width: calc(100% + 6px); + transform: translate(-3px, 3px); + height: 30px; + position: absolute; + left: 0; + bottom: 0; + background: linear-gradient(rgba(0, 0, 0, 0), white); + + background: rgb(255, 255, 255); + background: linear-gradient(0deg, rgba(255, 255, 255, 1) 10%, rgba(255, 255, 255, 0) 100%); + } + + &.is-body-style-type-card { + .excerpt-content:after { + transform: translate(-3px, 13px); + background: linear-gradient(0deg, rgba(255, 255, 255, 1) 50%, rgba(255, 255, 255, 0) 100%); + } + } + + &.is-body-style-type-retro-text-file { + .excerpt-content:after { + height: 62px; + } + } + + &.is-body-style-type-modern-text-file { + .excerpt-content:after { + width: calc(100% + 20px); + transform: translate(-10px, 22px); + height: 61px; + background: linear-gradient(0deg, white 50%, rgba(255, 255, 255, 0) 100%); + } + } + + &.is-body-style-type-command-line, + &.is-body-style-type-term-white, + &.is-body-style-type-term-green, + &.is-body-style-type-term-orange { + .excerpt-content:after { + background: linear-gradient(0deg, white 50%, rgba(255, 255, 255, 0) 100%); + } + } + + &.is-body-style-type-term-c64 { + .excerpt-content:after { + width: calc(100% + 50px); + transform: translate(-25px, 20px); + height: 45px; + background: linear-gradient(0deg, white 50%, rgba(255, 255, 255, 0) 100%); + } + } + } + + &.is-top-edge-type-jagged { + .excerpt-content:before { + content: ' '; + width: 100%; + height: 10px; + position: absolute; + left: 0; + top: 0; + transform: rotate(180deg) translate(0, 10px); + } + + &.is-body-style-type-bordered-box { + .excerpt-content { + border-top-left-radius: 0; + border-top-right-radius: 0; + } + } + } + + &.is-bottom-edge-type-jagged { + .excerpt-content:after { + content: ' '; + width: 100%; + height: 10px; + position: absolute; + left: 0; + bottom: 0; + transform: translate(0, 10px); + } + + &.is-body-style-type-bordered-box { + .excerpt-content { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + + // &:after { + // border-right: 1px solid $color-shadow-light; + // border-image: linear-gradient( + // to bottom, + // $color-shadow-light 50%, + // rgba(0, 0, 0, 0) 50% + // ); /* to top - at 50% transparent */ + // border-image-slice: 1; + // } + } + } + } + + &.is-top-edge-type-jagged.is-body-style-type-filled-box, + &.is-top-edge-type-jagged.is-body-style-type-card { + .excerpt-content:before { + background: url(./jagged-edge-filled-box.svg); + } + } + + &.is-top-edge-type-jagged.is-body-style-type-bordered-box { + .excerpt-content:before { + background: url(./jagged-edge-bordered-box.svg); + } + } + + &.is-top-edge-type-jagged.is-body-style-type-white-paper { + .excerpt-content:before { + background: url(./jagged-edge-white-paper.svg); + } + } + + &.is-top-edge-type-jagged.is-body-style-type-modern-paper { + .excerpt-content:before { + background: url(./jagged-edge-modern-paper.svg); + } + } + + &.is-top-edge-type-jagged.is-body-style-type-light-yellow-paper { + .excerpt-content:before { + background: url(./jagged-edge-light-yellow-paper.svg); + } + } + + &.is-top-edge-type-jagged.is-body-style-type-dark-yellow-paper { + .excerpt-content:before { + background: url(./jagged-edge-dark-yellow-paper.svg); + } + } + + &.is-top-edge-type-jagged.is-body-style-type-aged-paper { + .excerpt-content:before { + background: url(./jagged-edge-aged-paper.svg); + } + } + + &.is-bottom-edge-type-jagged.is-body-style-type-filled-box, + &.is-bottom-edge-type-jagged.is-body-style-type-card { + .excerpt-content:after { + background: url(./jagged-edge-filled-box.svg); + } + } + + &.is-bottom-edge-type-jagged.is-body-style-type-bordered-box { + .excerpt-content:after { + background: url(./jagged-edge-bordered-box.svg); + } + } + + &.is-bottom-edge-type-jagged.is-body-style-type-white-paper { + .excerpt-content:after { + background: url(./jagged-edge-white-paper.svg); + } + } + + &.is-bottom-edge-type-jagged.is-body-style-type-modern-paper { + .excerpt-content:after { + background: url(./jagged-edge-modern-paper.svg); + } + } + + &.is-bottom-edge-type-jagged.is-body-style-type-light-yellow-paper { + .excerpt-content:after { + background: url(./jagged-edge-light-yellow-paper.svg); + } + } + + &.is-bottom-edge-type-jagged.is-body-style-type-dark-yellow-paper { + .excerpt-content:after { + background: url(./jagged-edge-dark-yellow-paper.svg); + } + } + + &.is-bottom-edge-type-jagged.is-body-style-type-aged-paper { + .excerpt-content:after { + background: url(./jagged-edge-aged-paper.svg); + } + } +} diff --git a/packages/obonode/obojobo-chunks-excerpt/viewer.js b/packages/obonode/obojobo-chunks-excerpt/viewer.js new file mode 100644 index 0000000000..e8ef15bec3 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/viewer.js @@ -0,0 +1,10 @@ +import Common from 'obojobo-document-engine/src/scripts/common' +import ViewerComponent from './viewer-component' +import adapter from './adapter' + +Common.Registry.registerModel('ObojoboDraft.Chunks.Excerpt', { + adapter, + componentClass: ViewerComponent, + default: true, + type: 'chunk' +}) diff --git a/packages/obonode/obojobo-chunks-excerpt/viewer.scss b/packages/obonode/obojobo-chunks-excerpt/viewer.scss new file mode 100644 index 0000000000..22123678fe --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/viewer.scss @@ -0,0 +1,13 @@ +@import '../obojobo-document-engine/src/scss'; + +.obojobo-draft--chunks--excerpt { + pre { + font-size: 1em; + padding-left: 2em; + padding-right: 2em; + } + + .text { + display: block; + } +} From 7104cd8ee629a2f82c2b327b89c6cf520b47ad1d Mon Sep 17 00:00:00 2001 From: Deundre Williams Date: Wed, 2 Nov 2022 10:36:54 -0400 Subject: [PATCH 05/47] add more tests --- .../editor-component.test.js.snap | 12 ++ .../__snapshots__/icon.test.js.snap | 66 ++++++++++ .../viewer-component.test.js.snap | 118 ++++++++++++++++++ .../editor-component.test.js | 58 +++++++++ .../editor-registration.js | 40 ++---- .../obonode/obojobo-chunks-excerpt/editor.js | 2 +- .../obojobo-chunks-excerpt/editor.test.js | 24 ++++ .../obojobo-chunks-excerpt/icon.test.js | 13 ++ .../viewer-component.js | 2 +- .../viewer-component.test.js | 86 +++++++++++++ 10 files changed, 388 insertions(+), 33 deletions(-) create mode 100644 packages/obonode/obojobo-chunks-excerpt/__snapshots__/editor-component.test.js.snap create mode 100644 packages/obonode/obojobo-chunks-excerpt/__snapshots__/icon.test.js.snap create mode 100644 packages/obonode/obojobo-chunks-excerpt/__snapshots__/viewer-component.test.js.snap create mode 100644 packages/obonode/obojobo-chunks-excerpt/editor-component.test.js create mode 100644 packages/obonode/obojobo-chunks-excerpt/editor.test.js create mode 100644 packages/obonode/obojobo-chunks-excerpt/icon.test.js create mode 100644 packages/obonode/obojobo-chunks-excerpt/viewer-component.test.js diff --git a/packages/obonode/obojobo-chunks-excerpt/__snapshots__/editor-component.test.js.snap b/packages/obonode/obojobo-chunks-excerpt/__snapshots__/editor-component.test.js.snap new file mode 100644 index 0000000000..916816f7f7 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/__snapshots__/editor-component.test.js.snap @@ -0,0 +1,12 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Excerpt Node Node builds the expected component 1`] = ` +
    +
    +
    +
    +
    +`; diff --git a/packages/obonode/obojobo-chunks-excerpt/__snapshots__/icon.test.js.snap b/packages/obonode/obojobo-chunks-excerpt/__snapshots__/icon.test.js.snap new file mode 100644 index 0000000000..5cf97abf1c --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/__snapshots__/icon.test.js.snap @@ -0,0 +1,66 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Excerpt Icon Icon 1`] = ` + + + + + + + + + + + + + + + + + + + + +`; diff --git a/packages/obonode/obojobo-chunks-excerpt/__snapshots__/viewer-component.test.js.snap b/packages/obonode/obojobo-chunks-excerpt/__snapshots__/viewer-component.test.js.snap new file mode 100644 index 0000000000..3db84d68ff --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/__snapshots__/viewer-component.test.js.snap @@ -0,0 +1,118 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Excerpt Excerpt component 1`] = ` +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +`; diff --git a/packages/obonode/obojobo-chunks-excerpt/editor-component.test.js b/packages/obonode/obojobo-chunks-excerpt/editor-component.test.js new file mode 100644 index 0000000000..875493b65f --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/editor-component.test.js @@ -0,0 +1,58 @@ +import React from 'react' +import renderer from 'react-test-renderer' + +import Excerpt from './editor-component' + +jest.mock('slate') +jest.mock('slate-react') +jest.mock( + 'obojobo-document-engine/src/scripts/oboeditor/components/node/with-slate-wrapper', + () => item => item +) +jest.mock( + 'obojobo-document-engine/src/scripts/oboeditor/components/node/editor-component', + () => props =>
    {props.children}
    +) + +describe('Excerpt Node', () => { + test('Node builds the expected component', () => { + + const content = { + bodyStyle: 'filled-box', + width: 'medium', + font: 'sans', + lineHeight: 'moderate', + fontSize: 'smaller', + topEdge: 'normal', + bottomEdge: 'normal', + effect: false + } + + const component = renderer.create( + { + return {} + } + }, + content: {} + }} + parent={{ + getPath: () => ({ + get: () => 0 + }), + nodes: { + size: 2 + } + }} + element={{ + content: content, + }} + /> + ) + const tree = component.toJSON() + + expect(tree).toMatchSnapshot() + }) +}) diff --git a/packages/obonode/obojobo-chunks-excerpt/editor-registration.js b/packages/obonode/obojobo-chunks-excerpt/editor-registration.js index 74d0612b5e..3d7665e276 100644 --- a/packages/obonode/obojobo-chunks-excerpt/editor-registration.js +++ b/packages/obonode/obojobo-chunks-excerpt/editor-registration.js @@ -1,48 +1,26 @@ -import { Editor, Node, Element, Transforms, Text } from 'slate' -import { ReactEditor } from 'slate-react' -import Converter from './converter' -import Icon from './icon' import KeyDownUtil from 'obojobo-document-engine/src/scripts/oboeditor/util/keydown-util' -import Line from './components/line/editor-component' -import ExcerptContent from './components/excerpt-content/editor-component' -import Citation from './components/citation/editor-component' -import EditorComponent from './editor-component' import React from 'react' -import emptyNode from './empty-node.json' - -import normalizeNode from './changes/normalize-node' +import { Editor, Element, Node, Text, Transforms } from 'slate' import decreaseIndent from './changes/decrease-indent' import increaseIndent from './changes/increase-indent' import indentOrTab from './changes/indent-or-tab' +import Citation from './components/citation/editor-component' +import ExcerptContent from './components/excerpt-content/editor-component' +import Line from './components/line/editor-component' +import Converter from './converter' +import EditorComponent from './editor-component' +import emptyNode from './empty-node.json' +import Icon from './icon' + const EXCERPT_NODE = 'ObojoboDraft.Chunks.Excerpt' const EXCERPT_CONTENT = 'ObojoboDraft.Chunks.Excerpt.ExcerptContent' const CITE_TEXT_NODE = 'ObojoboDraft.Chunks.Excerpt.CitationText' const CITE_LINE_NODE = 'ObojoboDraft.Chunks.Excerpt.CitationLine' -const EXCERPT_TEXT_NODE = 'ObojoboDraft.Chunks.Excerpt.ExcerptText' const EXCERPT_TEXT_LINE_NODE = 'ObojoboDraft.Chunks.Excerpt.ExcerptLine' const TEXT_NODE = 'ObojoboDraft.Chunks.Text' const TEXT_LINE_NODE = 'ObojoboDraft.Chunks.Text.TextLine' -// { -/* - - - - - - - - -{ - type: "Excerpt", - content: { - body: [ {} ], - citation: [ {} ] - } -} */ -// } - const Excerpt = { name: EXCERPT_NODE, icon: Icon, diff --git a/packages/obonode/obojobo-chunks-excerpt/editor.js b/packages/obonode/obojobo-chunks-excerpt/editor.js index a4998dcd38..16b23b21b5 100644 --- a/packages/obonode/obojobo-chunks-excerpt/editor.js +++ b/packages/obonode/obojobo-chunks-excerpt/editor.js @@ -2,6 +2,6 @@ import Common from 'obojobo-document-engine/src/scripts/common' import EditorNode from './editor-registration' -console.log('registering editor model for excerpt') +// console.log('registering editor model for excerpt') // register Common.Registry.registerEditorModel(EditorNode) diff --git a/packages/obonode/obojobo-chunks-excerpt/editor.test.js b/packages/obonode/obojobo-chunks-excerpt/editor.test.js new file mode 100644 index 0000000000..9eaa63dab4 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/editor.test.js @@ -0,0 +1,24 @@ +jest.mock('obojobo-document-engine/src/scripts/common/index', () => ({ + Registry: { + registerEditorModel: jest.fn() + } +})) + +jest.mock('./editor-registration', () => ({ EditorNode: 1 })) + +import Common from 'obojobo-document-engine/src/scripts/common/index' + +describe('Excerpt editor script', () => { + test('registers node', () => { + // shouldn't have been called yet + expect(Common.Registry.registerEditorModel).toHaveBeenCalledTimes(0) + + require('./editor') + const EditorRegistration = require('./editor-registration') + + // the editor script should have registered the model + expect(Common.Registry.registerEditorModel).toHaveBeenCalledTimes(1) + + expect(Common.Registry.registerEditorModel).toHaveBeenCalledWith(EditorRegistration) + }) +}) diff --git a/packages/obonode/obojobo-chunks-excerpt/icon.test.js b/packages/obonode/obojobo-chunks-excerpt/icon.test.js new file mode 100644 index 0000000000..2b48224b33 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/icon.test.js @@ -0,0 +1,13 @@ +import React from 'react' +import renderer from 'react-test-renderer' + +import Icon from './icon' + +describe('Excerpt Icon', () => { + test('Icon', () => { + const component = renderer.create() + const tree = component.toJSON() + + expect(tree).toMatchSnapshot() + }) +}) diff --git a/packages/obonode/obojobo-chunks-excerpt/viewer-component.js b/packages/obonode/obojobo-chunks-excerpt/viewer-component.js index 678d82f407..b6d5815cf2 100644 --- a/packages/obonode/obojobo-chunks-excerpt/viewer-component.js +++ b/packages/obonode/obojobo-chunks-excerpt/viewer-component.js @@ -11,7 +11,7 @@ const { TextChunk } = Common.chunk const Excerpt = props => { const modelState = props.model.modelState - console.log(modelState) + // console.log(modelState) return ( diff --git a/packages/obonode/obojobo-chunks-excerpt/viewer-component.test.js b/packages/obonode/obojobo-chunks-excerpt/viewer-component.test.js new file mode 100644 index 0000000000..aa675e1c11 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/viewer-component.test.js @@ -0,0 +1,86 @@ +import React from 'react' +import renderer from 'react-test-renderer' + +import Excerpt from './viewer-component' +import OboModel from 'obojobo-document-engine/src/scripts/common/models/obo-model' + +const chunkJSON = { + id: 'id', + type: "ObojoboDraft.Chunks.Excerpt", + content: { + bodyStyle: "filled-box", + width: "medium", + font: "sans", + lineHeight: "moderate", + fontSize: "smaller", + topEdge: "normal", + bottomEdge: "normal", + effect: false + }, + children: [ + { + type: "ObojoboDraft.Chunks.Excerpt", + subtype: "ObojoboDraft.Chunks.Excerpt.ExcerptContent", + content: {}, + children: [ + { + type: "ObojoboDraft.Chunks.Text", + content: {}, + children: [ + { + type: "ObojoboDraft.Chunks.Text", + subtype: "ObojoboDraft.Chunks.Text.TextLine", + content: { + indent: 0 + }, + children: [ + { + text: "" + } + ] + } + ] + } + ] + }, + { + type: "ObojoboDraft.Chunks.Excerpt", + subtype: "ObojoboDraft.Chunks.Excerpt.CitationText", + content: { + indent: 0, + hangingIndent: 0 + }, + children: [ + { + type: "ObojoboDraft.Chunks.Excerpt", + subtype: "ObojoboDraft.Chunks.Excerpt.CitationLine", + content: { + indent: 0, + hangingIndent: 0, + align: "center" + }, + children: [ + { + text: "" + } + ] + } + ] + } + ] +} + +require('./viewer') // used to register this oboModel + +describe('Excerpt', () => { + test('Excerpt component', () => { + const model = OboModel.create(chunkJSON) + const moduleData = { + focusState: {} + } + const component = renderer.create() + const tree = component.toJSON() + + expect(tree).toMatchSnapshot() + }) +}) From 924dfb576775257db7bd1bd805433df8f7b85682 Mon Sep 17 00:00:00 2001 From: Deundre Williams Date: Mon, 7 Nov 2022 10:51:19 -0500 Subject: [PATCH 06/47] add adapter and viewer tests --- .../obonode/obojobo-chunks-excerpt/adapter.js | 177 ++++++++---------- .../obojobo-chunks-excerpt/adapter.test.js | 176 +++++++++++++++++ .../excerpt-content/editor-component.js | 44 ++--- .../obojobo-chunks-excerpt/viewer.test.js | 33 ++++ 4 files changed, 311 insertions(+), 119 deletions(-) create mode 100644 packages/obonode/obojobo-chunks-excerpt/adapter.test.js create mode 100644 packages/obonode/obojobo-chunks-excerpt/viewer.test.js diff --git a/packages/obonode/obojobo-chunks-excerpt/adapter.js b/packages/obonode/obojobo-chunks-excerpt/adapter.js index 25fe0e2c03..36ea1afe8c 100644 --- a/packages/obonode/obojobo-chunks-excerpt/adapter.js +++ b/packages/obonode/obojobo-chunks-excerpt/adapter.js @@ -1,119 +1,102 @@ import Common from 'obojobo-document-engine/src/scripts/common' -const { TextGroupAdapter } = Common.chunk.textChunk +// const { TextGroupAdapter } = Common.chunk.textChunk const { TextGroup } = Common.textGroup const Adapter = { - - construct(model, attrs) { - - model.setStateProp('font', 'sans', p => p.toLowerCase(), - 'serif', - 'sans', - 'monospace', - 'times-new-roman', - 'georgia', - 'helvetica', - 'courier', - 'palatino' - ) - - model.setStateProp('bodyStyle', 'filled-box', p => p.toLowerCase(), - 'none', - 'filled-box', - 'bordered-box', - 'card', - 'white-paper', - 'modern-paper', - 'light-yellow-paper', - 'dark-yellow-paper', - 'aged-paper', - 'modern-text-file', - 'retro-text-file', - 'command-line', - 'term-white', - 'term-green', - 'term-orange', - 'term-c64' - ) - - model.setStateProp('width', 'medium', p => p.toLowerCase(), - 'large', - 'medium', - 'small', - 'tiny' - ) - - model.setStateProp('fontSize', 'smaller', p => p.toLowerCase(), - 'smaller', - 'regular', - 'larger' - ) - - model.setStateProp('lineHeight', 'moderate', p => p.toLowerCase(), - 'compact', - 'moderate', - 'generous' - ) + construct(model, attrs) { + model.setStateProp( + 'font', + 'sans', + p => p.toLowerCase(), + 'serif', + 'sans', + 'monospace', + 'times-new-roman', + 'georgia', + 'helvetica', + 'courier', + 'palatino' + ) + + model.setStateProp( + 'bodyStyle', + 'filled-box', + p => p.toLowerCase(), + 'none', + 'filled-box', + 'bordered-box', + 'card', + 'white-paper', + 'modern-paper', + 'light-yellow-paper', + 'dark-yellow-paper', + 'aged-paper', + 'modern-text-file', + 'retro-text-file', + 'command-line', + 'term-white', + 'term-green', + 'term-orange', + 'term-c64' + ) + + model.setStateProp('width', 'medium', p => p.toLowerCase(), 'large', 'medium', 'small', 'tiny') + + model.setStateProp('fontSize', 'smaller', p => p.toLowerCase(), 'smaller', 'regular', 'larger') + + model.setStateProp( + 'lineHeight', + 'moderate', + p => p.toLowerCase(), + 'compact', + 'moderate', + 'generous' + ) model.setStateProp('effect', false) - if (attrs && attrs.content) { + // console.log('attrs:', attrs) - if (attrs.content.citation) { - model.modelState.citation = TextGroup.fromDescriptor(attrs.content.citation, 1, {}) - // model.modelState.citation.isBlank = false; - } else { - model.modelState.citation = TextGroup.create(1, {}) - } + if (attrs && attrs.content) { + if (attrs.content.citation) { - if (attrs.content.font) { - model.modelState.font = attrs.content.font - } + // console.log('building textGroup from ', attrs.content.citation, 1, {}) - if (attrs.content.bodyStyle) { - model.modelState.bodyStyle = attrs.content.bodyStyle + model.modelState.citation = TextGroup.fromDescriptor(attrs.content.citation, 1, {}) + } else { + model.modelState.citation = TextGroup.create(1, {}) } - if (attrs.content.width) { - model.modelState.width = attrs.content.width - } + // console.log('set citation to ', model.modelState.citation) - if (attrs.content.fontSize) { - model.modelState.fontSize = attrs.content.fontSize - } + if (attrs.content.font) { + model.modelState.font = attrs.content.font + } - if (attrs.content.lineHeight) { - model.modelState.lineHeight = attrs.content.lineHeight - } + if (attrs.content.bodyStyle) { + model.modelState.bodyStyle = attrs.content.bodyStyle + } - if (attrs.content.effect) { - model.modelState.effect = attrs.content.effect - } + if (attrs.content.width) { + model.modelState.width = attrs.content.width + } + + if (attrs.content.fontSize) { + model.modelState.fontSize = attrs.content.fontSize + } + + if (attrs.content.lineHeight) { + model.modelState.lineHeight = attrs.content.lineHeight + } + if (attrs.content.effect) { + model.modelState.effect = attrs.content.effect + } } - }, - - clone(model, clone) { - TextGroupAdapter.clone(model, clone) - clone.modelState.font = model.modelState.font - clone.modelState.bodyStyle = model.modelState.bodyStyle - clone.modelState.width = model.modelState.width - clone.modelState.fontSize = model.modelState.fontSize - clone.modelState.lineHeight = model.modelState.lineHeight - clone.modelState.effect = model.modelState.effect - }, - - toJson(model, json) { - TextGroupAdapter.toJSON(model, json) - json.modelState.font = model.modelState.font - json.modelState.bodyStyle = model.modelState.bodyStyle - json.modelState.width = model.modelState.width - json.modelState.fontSize = model.modelState.fontSize - json.modelState.lineHeight = model.modelState.lineHeight - json.modelState.effect = model.modelState.effect - } + // console.log("model: ", model) + }, } diff --git a/packages/obonode/obojobo-chunks-excerpt/adapter.test.js b/packages/obonode/obojobo-chunks-excerpt/adapter.test.js new file mode 100644 index 0000000000..6e6e592e98 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/adapter.test.js @@ -0,0 +1,176 @@ +jest.mock('obojobo-document-engine/src/scripts/common/models/obo-model', () => { + return require('obojobo-document-engine/__mocks__/obo-model-adapter-mock').default +}) +import Common from 'obojobo-document-engine/src/scripts/common' +import OboModel from 'obojobo-document-engine/src/scripts/common/models/obo-model' +import ExcerptAdapter from './adapter' +const { TextGroup } = Common.textGroup +// const { TextGroupAdapter } = Common.chunk.textChunk + + +describe('Excerpt adapter', () => { + + const defaultState = { + bodyStyle: 'filled-box', + width: 'medium', + font: 'sans', + lineHeight: 'moderate', + fontSize: 'smaller', + effect: false, + } + + const defaultStateWithCitation = { + ...defaultState, + citation: TextGroup.create(1, {}) + } + + // const defaultAttrs = { + // bodyStyle: + // } + + test('construct builds without attributes', () => { + const model = new OboModel({}) + + ExcerptAdapter.construct(model) + + expect(model.modelState).toEqual(defaultState) + }) + + test('construct builds with citation attributes', () => { + const attrs = { + content: { + citation: [ + { + data: { align: 'center', hangingIndent: 0, indent: 0 }, + text: { styleList: null, value: 'Placeholder text' } + } + ] + } + } + + const model = new OboModel(attrs) + const expected = { + ...defaultStateWithCitation, + citation: TextGroup.fromDescriptor(attrs.content.citation, 1, {}) + } + ExcerptAdapter.construct(model, attrs) + + expect(model.modelState).toEqual(expected) + }) + + test('construct builds with empty content object', () => { + + const attrs = { + content: { } + } + + const model = new OboModel(attrs) + const expected = defaultStateWithCitation + ExcerptAdapter.construct(model, attrs) + + expect(model.modelState).toEqual(expected) + }) + + test('construct builds with font attribute', () => { + const attrs = { + content: { + font: 'monospace', + } + } + + const model = new OboModel(attrs) + const expected = { + ...defaultStateWithCitation, + font: 'monospace' + } + ExcerptAdapter.construct(model, attrs) + + expect(model.modelState).toEqual(expected) + }) + + test('construct builds with bodyStyle attribute', () => { + const attrs = { + content: { + bodyStyle: 'card', + } + } + + const model = new OboModel(attrs) + const expected = { + ...defaultStateWithCitation, + bodyStyle: 'card' + } + ExcerptAdapter.construct(model, attrs) + + expect(model.modelState).toEqual(expected) + }) + + test('construct builds with width attribute', () => { + const attrs = { + content: { + width: 'large', + } + } + + const model = new OboModel(attrs) + const expected = { + ...defaultStateWithCitation, + width: 'large' + } + ExcerptAdapter.construct(model, attrs) + + expect(model.modelState).toEqual(expected) + }) + + test('construct builds with fontSize attribute', () => { + const attrs = { + content: { + fontSize: 'smaller', + } + } + + const model = new OboModel(attrs) + const expected = { + ...defaultStateWithCitation, + fontSize: 'smaller' + } + ExcerptAdapter.construct(model, attrs) + + expect(model.modelState).toEqual(expected) + }) + + test('construct builds with lineHeight attribute', () => { + const attrs = { + content: { + lineHeight: 'generous', + } + } + + const model = new OboModel(attrs) + const expected = { + ...defaultStateWithCitation, + lineHeight: 'generous' + } + ExcerptAdapter.construct(model, attrs) + + expect(model.modelState).toEqual(expected) + }) + + test('construct builds with effect attribute', () => { + const attrs = { + content: { + effect: true, + } + } + + const model = new OboModel(attrs) + const expected = { + ...defaultStateWithCitation, + effect: true + } + ExcerptAdapter.construct(model, attrs) + + expect(model.modelState).toEqual(expected) + }) + +}) diff --git a/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.js b/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.js index 806bf15ba2..814507df12 100644 --- a/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.js +++ b/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.js @@ -1,12 +1,12 @@ // import '../../viewer-component.scss' +import withSlateWrapper from 'obojobo-document-engine/src/scripts/oboeditor/components/node/with-slate-wrapper' import React from 'react' +import { Editor, Range, Transforms } from 'slate' import { ReactEditor } from 'slate-react' -import { Editor, Transforms, Range } from 'slate' -import withSlateWrapper from 'obojobo-document-engine/src/scripts/oboeditor/components/node/with-slate-wrapper' +import getPresetProps from '../../get-preset-props' import EdgeControls from '../edge-controls' import ExcerptEditControls from '../excerpt-edit-controls' -import getPresetProps from '../../get-preset-props' const getEdgeOptionsForBodyStyle = bodyStyle => { switch (bodyStyle) { @@ -29,28 +29,28 @@ const getEdgeOptionsForBodyStyle = bodyStyle => { } const ExcerptContent = props => { - const onChangeEdge = (edge, edgeType) => { - const [parent, parentPath] = Editor.parent( - props.editor, - ReactEditor.findPath(props.editor, props.element) - ) - const parentContent = parent.content - const newContent = { ...parentContent, [edge]: edgeType } - - // We update both this node and the parent node, as they both maintain copies of the same - // content - necessary to get both to re-render - Transforms.setNodes(props.editor, { content: { ...newContent } }, { at: parentPath }) - Transforms.setNodes( - props.editor, - { content: { ...newContent } }, - { at: ReactEditor.findPath(props.editor, props.element) } - ) - } + // const onChangeEdge = (edge, edgeType) => { + // const [parent, parentPath] = Editor.parent( + // props.editor, + // ReactEditor.findPath(props.editor, props.element) + // ) + // const parentContent = parent.content + // const newContent = { ...parentContent, [edge]: edgeType } + + // // We update both this node and the parent node, as they both maintain copies of the same + // // content - necessary to get both to re-render + // Transforms.setNodes(props.editor, { content: { ...newContent } }, { at: parentPath }) + // Transforms.setNodes( + // props.editor, + // { content: { ...newContent } }, + // { at: ReactEditor.findPath(props.editor, props.element) } + // ) + // } // I was moving these methods over here, and then maybe I can no longer have to duplicate // properties and just use properties on this one, Excerpt Content?! const onChangePreset = presetValue => { - const [parent, parentPath] = Editor.parent( + const [, parentPath] = Editor.parent( props.editor, ReactEditor.findPath(props.editor, props.element) ) @@ -68,7 +68,7 @@ const ExcerptContent = props => { } const onChangeContentValue = (contentValueName, value) => { - const [parent, parentPath] = Editor.parent( + const [, parentPath] = Editor.parent( props.editor, ReactEditor.findPath(props.editor, props.element) ) diff --git a/packages/obonode/obojobo-chunks-excerpt/viewer.test.js b/packages/obonode/obojobo-chunks-excerpt/viewer.test.js new file mode 100644 index 0000000000..9a5018d475 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/viewer.test.js @@ -0,0 +1,33 @@ +jest.mock('obojobo-document-engine/src/scripts/common/index', () => ({ + Registry: { + registerModel: jest.fn() + }, + chunk: { + textChunk: { + TextGroupAdapter: jest.fn() + } + }, + textGroup: { + TextGroup: jest.fn() + } +})) + +jest.mock('./viewer-component', () => ({})) + +const Common = require('obojobo-document-engine/src/scripts/common/index') +import Adapter from './adapter' + +// include the script we're testing, it registers the model +import './viewer' +import ViewerComponent from './viewer-component' + +describe('ObojoboDraft.Chunks.Code registration', () => { + test('registerModel registers expected vars', () => { + const register = Common.Registry.registerModel.mock.calls[0] + expect(register[0]).toBe('ObojoboDraft.Chunks.Excerpt') + expect(register[1]).toHaveProperty('type', 'chunk') + expect(register[1]).toHaveProperty('default', true) + expect(register[1]).toHaveProperty('adapter', Adapter) + expect(register[1]).toHaveProperty('componentClass', ViewerComponent) + }) +}) From 1ca57ce0f08d06b9c28d65f01663ad1da19d13e0 Mon Sep 17 00:00:00 2001 From: Deundre Williams Date: Tue, 15 Nov 2022 09:48:00 -0500 Subject: [PATCH 07/47] increase coverage --- .../editor-component.test.js.snap | 13 +- .../viewer-component.test.js.snap | 260 +++++++++++- .../obonode/obojobo-chunks-excerpt/adapter.js | 18 +- .../obojobo-chunks-excerpt/adapter.test.js | 180 +++++---- .../changes/decrease-indent.test.js | 47 +++ .../changes/increase-indent.test.js | 48 +++ .../changes/indent-or-tab.test.js | 83 ++++ .../__snapshots__/edge-controls.test.js.snap | 47 +++ .../excerpt-edit-controls.test.js.snap | 299 ++++++++++++++ .../__snapshots__/radio-icons.test.js.snap | 77 ++++ .../editor-component.test.js.snap | 15 + .../citation/editor-component.test.js | 21 + .../components/edge-controls.test.js | 113 ++++++ .../editor-component.test.js.snap | 374 ++++++++++++++++++ .../excerpt-content/editor-component.js | 6 + .../excerpt-content/editor-component.test.js | 301 ++++++++++++++ .../components/excerpt-edit-controls.js | 18 +- .../components/excerpt-edit-controls.test.js | 320 +++++++++++++++ .../editor-component.test.js.snap | 19 + .../components/line/editor-component.test.js | 29 ++ .../components/radio-icons.test.js | 82 ++++ .../obojobo-chunks-excerpt/converter.js | 20 +- .../editor-component.js | 83 +--- .../editor-component.test.js | 77 +++- .../get-preset-props.test.js | 303 ++++++++++++++ .../viewer-component.js | 4 +- .../viewer-component.test.js | 167 +++++--- 27 files changed, 2732 insertions(+), 292 deletions(-) create mode 100644 packages/obonode/obojobo-chunks-excerpt/changes/decrease-indent.test.js create mode 100644 packages/obonode/obojobo-chunks-excerpt/changes/increase-indent.test.js create mode 100644 packages/obonode/obojobo-chunks-excerpt/changes/indent-or-tab.test.js create mode 100644 packages/obonode/obojobo-chunks-excerpt/components/__snapshots__/edge-controls.test.js.snap create mode 100644 packages/obonode/obojobo-chunks-excerpt/components/__snapshots__/excerpt-edit-controls.test.js.snap create mode 100644 packages/obonode/obojobo-chunks-excerpt/components/__snapshots__/radio-icons.test.js.snap create mode 100644 packages/obonode/obojobo-chunks-excerpt/components/citation/__snapshots__/editor-component.test.js.snap create mode 100644 packages/obonode/obojobo-chunks-excerpt/components/citation/editor-component.test.js create mode 100644 packages/obonode/obojobo-chunks-excerpt/components/edge-controls.test.js create mode 100644 packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/__snapshots__/editor-component.test.js.snap create mode 100644 packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.test.js create mode 100644 packages/obonode/obojobo-chunks-excerpt/components/excerpt-edit-controls.test.js create mode 100644 packages/obonode/obojobo-chunks-excerpt/components/line/__snapshots__/editor-component.test.js.snap create mode 100644 packages/obonode/obojobo-chunks-excerpt/components/line/editor-component.test.js create mode 100644 packages/obonode/obojobo-chunks-excerpt/components/radio-icons.test.js create mode 100644 packages/obonode/obojobo-chunks-excerpt/get-preset-props.test.js diff --git a/packages/obonode/obojobo-chunks-excerpt/__snapshots__/editor-component.test.js.snap b/packages/obonode/obojobo-chunks-excerpt/__snapshots__/editor-component.test.js.snap index 916816f7f7..2aa4613678 100644 --- a/packages/obonode/obojobo-chunks-excerpt/__snapshots__/editor-component.test.js.snap +++ b/packages/obonode/obojobo-chunks-excerpt/__snapshots__/editor-component.test.js.snap @@ -1,10 +1,19 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Excerpt Node Node builds component while selected with effect 1`] = ` +
    +
    +
    +
    +
    +`; + exports[`Excerpt Node Node builds the expected component 1`] = `
    diff --git a/packages/obonode/obojobo-chunks-excerpt/__snapshots__/viewer-component.test.js.snap b/packages/obonode/obojobo-chunks-excerpt/__snapshots__/viewer-component.test.js.snap index 3db84d68ff..5c3b4c48be 100644 --- a/packages/obonode/obojobo-chunks-excerpt/__snapshots__/viewer-component.test.js.snap +++ b/packages/obonode/obojobo-chunks-excerpt/__snapshots__/viewer-component.test.js.snap @@ -25,10 +25,10 @@ exports[`Excerpt Excerpt component 1`] = `
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + + + +`; + +exports[`Excerpt Excerpt component with citation 1`] = ` +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + + + + Placeholder text + + + + + +
    +
    +
    +`; + +exports[`Excerpt Excerpt component with effect 1`] = ` +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    { - - const defaultState = { - bodyStyle: 'filled-box', - width: 'medium', - font: 'sans', - lineHeight: 'moderate', - fontSize: 'smaller', - effect: false, - } - - const defaultStateWithCitation = { - ...defaultState, - citation: TextGroup.create(1, {}) - } - - // const defaultAttrs = { - // bodyStyle: - // } + const defaultState = { + bodyStyle: 'filled-box', + width: 'medium', + font: 'sans', + lineHeight: 'moderate', + fontSize: 'smaller', + effect: false + } + + const defaultStateWithCitation = { + ...defaultState, + citation: TextGroup.create(1, {}) + } + + // const defaultAttrs = { + // bodyStyle: + // } test('construct builds without attributes', () => { const model = new OboModel({}) @@ -40,137 +38,135 @@ describe('Excerpt adapter', () => { const attrs = { content: { citation: [ - { - data: { align: 'center', hangingIndent: 0, indent: 0 }, - text: { styleList: null, value: 'Placeholder text' } - } - ] + { + data: { align: 'center', hangingIndent: 0, indent: 0 }, + text: { styleList: null, value: 'Placeholder text' } + } + ] } - } + } const model = new OboModel(attrs) const expected = { - ...defaultStateWithCitation, - citation: TextGroup.fromDescriptor(attrs.content.citation, 1, {}) - } - ExcerptAdapter.construct(model, attrs) + ...defaultStateWithCitation, + citation: TextGroup.fromDescriptor(attrs.content.citation, 1, {}) + } + ExcerptAdapter.construct(model, attrs) expect(model.modelState).toEqual(expected) - }) - - test('construct builds with empty content object', () => { + }) - const attrs = { - content: { } - } + test('construct builds with empty content object', () => { + const attrs = { + content: {} + } const model = new OboModel(attrs) const expected = defaultStateWithCitation - ExcerptAdapter.construct(model, attrs) + ExcerptAdapter.construct(model, attrs) expect(model.modelState).toEqual(expected) - }) + }) - test('construct builds with font attribute', () => { - const attrs = { + test('construct builds with font attribute', () => { + const attrs = { content: { - font: 'monospace', + font: 'monospace' } - } + } const model = new OboModel(attrs) const expected = { - ...defaultStateWithCitation, - font: 'monospace' - } - ExcerptAdapter.construct(model, attrs) + ...defaultStateWithCitation, + font: 'monospace' + } + ExcerptAdapter.construct(model, attrs) expect(model.modelState).toEqual(expected) - }) + }) - test('construct builds with bodyStyle attribute', () => { - const attrs = { + test('construct builds with bodyStyle attribute', () => { + const attrs = { content: { - bodyStyle: 'card', + bodyStyle: 'card' } - } + } const model = new OboModel(attrs) const expected = { - ...defaultStateWithCitation, - bodyStyle: 'card' - } - ExcerptAdapter.construct(model, attrs) + ...defaultStateWithCitation, + bodyStyle: 'card' + } + ExcerptAdapter.construct(model, attrs) expect(model.modelState).toEqual(expected) - }) + }) - test('construct builds with width attribute', () => { - const attrs = { + test('construct builds with width attribute', () => { + const attrs = { content: { - width: 'large', + width: 'large' } - } + } const model = new OboModel(attrs) const expected = { - ...defaultStateWithCitation, - width: 'large' - } - ExcerptAdapter.construct(model, attrs) + ...defaultStateWithCitation, + width: 'large' + } + ExcerptAdapter.construct(model, attrs) expect(model.modelState).toEqual(expected) - }) + }) - test('construct builds with fontSize attribute', () => { - const attrs = { + test('construct builds with fontSize attribute', () => { + const attrs = { content: { - fontSize: 'smaller', + fontSize: 'smaller' } - } + } const model = new OboModel(attrs) const expected = { - ...defaultStateWithCitation, - fontSize: 'smaller' - } - ExcerptAdapter.construct(model, attrs) + ...defaultStateWithCitation, + fontSize: 'smaller' + } + ExcerptAdapter.construct(model, attrs) expect(model.modelState).toEqual(expected) - }) + }) - test('construct builds with lineHeight attribute', () => { - const attrs = { + test('construct builds with lineHeight attribute', () => { + const attrs = { content: { - lineHeight: 'generous', + lineHeight: 'generous' } - } + } const model = new OboModel(attrs) const expected = { - ...defaultStateWithCitation, - lineHeight: 'generous' - } - ExcerptAdapter.construct(model, attrs) + ...defaultStateWithCitation, + lineHeight: 'generous' + } + ExcerptAdapter.construct(model, attrs) expect(model.modelState).toEqual(expected) - }) + }) - test('construct builds with effect attribute', () => { - const attrs = { + test('construct builds with effect attribute', () => { + const attrs = { content: { - effect: true, + effect: true } - } + } const model = new OboModel(attrs) const expected = { - ...defaultStateWithCitation, - effect: true - } - ExcerptAdapter.construct(model, attrs) + ...defaultStateWithCitation, + effect: true + } + ExcerptAdapter.construct(model, attrs) expect(model.modelState).toEqual(expected) - }) - + }) }) diff --git a/packages/obonode/obojobo-chunks-excerpt/changes/decrease-indent.test.js b/packages/obonode/obojobo-chunks-excerpt/changes/decrease-indent.test.js new file mode 100644 index 0000000000..934de2b599 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/changes/decrease-indent.test.js @@ -0,0 +1,47 @@ +import { Transforms } from 'slate' +import { ReactEditor } from 'slate-react' +import decreaseIndent from './decrease-indent' +jest.mock('slate-react') + +const EXCERPT_NODE = 'ObojoboDraft.Chunks.Excerpt' +const EXCERPT_LINE_NODE = 'ObojoboDraft.Chunks.Excerpt.ExcerptLine' + +describe('Decrease Excerpt Indent', () => { + test('decreaseIndent calls Transforms.setNodes', () => { + jest.spyOn(Transforms, 'setNodes').mockReturnValueOnce(true) + ReactEditor.findPath.mockReturnValueOnce([0]) + + const editor = { + children: [ + { + id: 'mockKey', + type: EXCERPT_NODE, + content: {}, + children: [ + { + type: EXCERPT_NODE, + subtype: EXCERPT_LINE_NODE, + content: { indent: 1 }, + children: [{ text: 'mockText' }] + } + ] + } + ], + selection: { + anchor: { path: [0, 0, 0], offset: 1 }, + focus: { path: [0, 0, 0], offset: 1 } + }, + isVoid: () => false + } + const event = { preventDefault: jest.fn() } + + decreaseIndent([editor.children[0], [0]], editor, event) + + expect(event.preventDefault).toHaveBeenCalled() + expect(Transforms.setNodes).toHaveBeenCalledWith( + editor, + { content: { indent: 0 } }, + { at: [0, 0] } + ) + }) +}) diff --git a/packages/obonode/obojobo-chunks-excerpt/changes/increase-indent.test.js b/packages/obonode/obojobo-chunks-excerpt/changes/increase-indent.test.js new file mode 100644 index 0000000000..f7887e2ef5 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/changes/increase-indent.test.js @@ -0,0 +1,48 @@ +import { Transforms } from 'slate' +import { ReactEditor } from 'slate-react' +jest.mock('slate-react') + +import increaseIndent from './increase-indent' + +const EXCERPT_NODE = 'ObojoboDraft.Chunks.Excerpt' +const EXCERPT_LINE_NODE = 'ObojoboDraft.Chunks.Excerpt.ExcerptLine' + +describe('Increase Code Indent', () => { + test('increaseIndent calls Transforms.setNodes', () => { + jest.spyOn(Transforms, 'setNodes').mockReturnValueOnce(true) + ReactEditor.findPath.mockReturnValueOnce([0]) + + const editor = { + children: [ + { + id: 'mockKey', + type: EXCERPT_NODE, + content: {}, + children: [ + { + type: EXCERPT_NODE, + subtype: EXCERPT_LINE_NODE, + content: { indent: 1 }, + children: [{ text: 'mockText' }] + } + ] + } + ], + selection: { + anchor: { path: [0, 0, 0], offset: 1 }, + focus: { path: [0, 0, 0], offset: 1 } + }, + isVoid: () => false + } + const event = { preventDefault: jest.fn() } + + increaseIndent([editor.children[0], [0]], editor, event) + + expect(event.preventDefault).toHaveBeenCalled() + expect(Transforms.setNodes).toHaveBeenCalledWith( + editor, + { content: { indent: 2 } }, + { at: [0, 0] } + ) + }) +}) diff --git a/packages/obonode/obojobo-chunks-excerpt/changes/indent-or-tab.test.js b/packages/obonode/obojobo-chunks-excerpt/changes/indent-or-tab.test.js new file mode 100644 index 0000000000..6f4bba0ed8 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/changes/indent-or-tab.test.js @@ -0,0 +1,83 @@ +import { Transforms } from 'slate' +import { ReactEditor } from 'slate-react' +jest.mock('slate-react') + +import indentOrTab from './indent-or-tab' + +const EXCERPT_NODE = 'ObojoboDraft.Chunks.Excerpt' +const EXCERPT_LINE_NODE = 'ObojoboDraft.Chunks.Excerpt.ExcerptLine' + +describe('Increase Code Indent', () => { + test('indentOrTab calls Transforms.setNodes', () => { + jest.spyOn(Transforms, 'setNodes').mockReturnValueOnce(true) + ReactEditor.findPath.mockReturnValueOnce([0]) + + const editor = { + children: [ + { + id: 'mockKey', + type: EXCERPT_NODE, + content: {}, + children: [ + { + type: EXCERPT_NODE, + subtype: EXCERPT_LINE_NODE, + content: { indent: 1 }, + children: [{ text: 'mockText' }] + } + ] + } + ], + selection: { + anchor: { path: [0, 0, 0], offset: 0 }, + focus: { path: [0, 0, 0], offset: 0 } + }, + isVoid: () => false + } + const event = { preventDefault: jest.fn() } + + indentOrTab([editor.children[0], [0]], editor, event) + + expect(event.preventDefault).toHaveBeenCalled() + expect(Transforms.setNodes).toHaveBeenCalledWith( + editor, + { content: { indent: 2 } }, + { at: [0, 0] } + ) + }) + + test('indentOrTab calls insertText', () => { + jest.spyOn(Transforms, 'setNodes').mockReturnValueOnce(true) + ReactEditor.findPath.mockReturnValueOnce([0]) + + const editor = { + children: [ + { + id: 'mockKey', + type: EXCERPT_NODE, + content: {}, + children: [ + { + type: EXCERPT_NODE, + subtype: EXCERPT_LINE_NODE, + content: { indent: 1 }, + children: [{ text: 'mockCode', b: true }] + } + ] + } + ], + selection: { + anchor: { path: [0, 0, 0], offset: 1 }, + focus: { path: [0, 0, 0], offset: 1 } + }, + isVoid: () => false, + insertText: jest.fn() + } + const event = { preventDefault: jest.fn() } + + indentOrTab([editor.children[0], [0]], editor, event) + + expect(event.preventDefault).toHaveBeenCalled() + expect(editor.insertText).toHaveBeenCalled() + }) +}) diff --git a/packages/obonode/obojobo-chunks-excerpt/components/__snapshots__/edge-controls.test.js.snap b/packages/obonode/obojobo-chunks-excerpt/components/__snapshots__/edge-controls.test.js.snap new file mode 100644 index 0000000000..d7e287c5c9 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/components/__snapshots__/edge-controls.test.js.snap @@ -0,0 +1,47 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Edge Controls Node builds the expected component 1`] = ` +
    +
    + + +
    +
    +`; + +exports[`Edge Controls Node builds the expected component with no available edge options 1`] = `null`; diff --git a/packages/obonode/obojobo-chunks-excerpt/components/__snapshots__/excerpt-edit-controls.test.js.snap b/packages/obonode/obojobo-chunks-excerpt/components/__snapshots__/excerpt-edit-controls.test.js.snap new file mode 100644 index 0000000000..f7075ffdf0 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/components/__snapshots__/excerpt-edit-controls.test.js.snap @@ -0,0 +1,299 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Excerpt Edit Controls More options Node builds the expected component with more options showing 1`] = `"
    "`; + +exports[`Excerpt Edit Controls More options Node builds the expected component with paper bodyStyle 1`] = `"
    "`; + +exports[`Excerpt Edit Controls More options Node builds the expected component with terminal bodyStyle 1`] = `"
    "`; + +exports[`Excerpt Edit Controls More options Node builds the expected component with text-file bodyStyle 1`] = `"
    "`; + +exports[`Excerpt Edit Controls Node builds the expected component 1`] = ` +
    +
    +
    +
      +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    +
    +
    +
    + +
    +
    +`; + +exports[`Excerpt Edit Controls Node builds the expected component using preset and handles change 1`] = `"
    "`; + +exports[`Excerpt Edit Controls Node handles INPUT mousedown event on attributes box 1`] = `"
    "`; + +exports[`Excerpt Edit Controls Node handles other mousedown event on attributes box 1`] = `"
    "`; diff --git a/packages/obonode/obojobo-chunks-excerpt/components/__snapshots__/radio-icons.test.js.snap b/packages/obonode/obojobo-chunks-excerpt/components/__snapshots__/radio-icons.test.js.snap new file mode 100644 index 0000000000..e6f0a300db --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/components/__snapshots__/radio-icons.test.js.snap @@ -0,0 +1,77 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Radio Icons Node builds the expected component 1`] = ` +
    +
    + + + +
    +
    +`; diff --git a/packages/obonode/obojobo-chunks-excerpt/components/citation/__snapshots__/editor-component.test.js.snap b/packages/obonode/obojobo-chunks-excerpt/components/citation/__snapshots__/editor-component.test.js.snap new file mode 100644 index 0000000000..1e3217a424 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/components/citation/__snapshots__/editor-component.test.js.snap @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Citation Node Node builds the expected component 1`] = ` + +
    + Child component +
    +
    + Child component +
    +
    + Child component +
    +
    +`; diff --git a/packages/obonode/obojobo-chunks-excerpt/components/citation/editor-component.test.js b/packages/obonode/obojobo-chunks-excerpt/components/citation/editor-component.test.js new file mode 100644 index 0000000000..799f1396e0 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/components/citation/editor-component.test.js @@ -0,0 +1,21 @@ +import Citation from './editor-component' +import renderer from 'react-test-renderer' +import React from 'react' + +describe('Citation Node', () => { + test('Node builds the expected component', () => { + const Child = () =>
    Child component
    + + const component = renderer.create( + + + + + + ) + + const tree = component.toJSON() + + expect(tree).toMatchSnapshot() + }) +}) diff --git a/packages/obonode/obojobo-chunks-excerpt/components/edge-controls.test.js b/packages/obonode/obojobo-chunks-excerpt/components/edge-controls.test.js new file mode 100644 index 0000000000..64ea6716e7 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/components/edge-controls.test.js @@ -0,0 +1,113 @@ +import EdgeControls from './edge-controls' +import renderer from 'react-test-renderer' +import React from 'react' +import { mount } from 'enzyme' + +describe('Edge Controls', () => { + const position = 'top' + const edges = ['normal', 'fade'] + const selectedEdge = 'normal' + const onChangeEdge = jest.fn() + + const defaultProps = { + position, + edges, + selectedEdge, + onChangeEdge + } + + afterEach(() => { + jest.clearAllMocks() + }) + + test('Node builds the expected component', () => { + const component = renderer.create() + + const tree = component.toJSON() + + expect(tree).toMatchSnapshot() + }) + + test('Node builds the expected component with no available edge options', () => { + const props = { + ...defaultProps, + edges: [] + } + + const component = renderer.create() + + const tree = component.toJSON() + + expect(tree).toMatchSnapshot() + }) + + test('Node handles mousedown', () => { + const component = mount() + + component + .find('label') + .at(1) + .simulate('mousedown') + + expect(onChangeEdge).toHaveBeenCalledWith(edges[1]) + }) + + test('Node handles change', () => { + const component = mount() + + component + .find('input') + .at(1) + .simulate('change') + + expect(onChangeEdge).toHaveBeenCalledWith(edges[1]) + }) + + // test('Handles mouse down event', () => { + + // const component = mount( + // + // ) + + // // console.log(component.debug()); + + // component + // .find('label') + // .at(0) + // .simulate('mousedown') + + // expect(onChange).toHaveBeenCalled() + // expect(onChange).toHaveBeenCalledWith('small') + + // }) + + // test('Handles change event', () => { + + // const component = mount( + // + // ) + + // // console.log(component.debug()); + + // component + // .find('input') + // .at(0) + // .simulate('change') + + // expect(onChange).toHaveBeenCalled() + // expect(onChange).toHaveBeenCalledWith('small') + + // }) +}) diff --git a/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/__snapshots__/editor-component.test.js.snap b/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/__snapshots__/editor-component.test.js.snap new file mode 100644 index 0000000000..58045bb8e0 --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/__snapshots__/editor-component.test.js.snap @@ -0,0 +1,374 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Excerpt Content Node Node builds component when selected and collapsed 1`] = ` +
    +
    +
    +
    +
      +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    • + +
    • +
    +
    +
    +
    + +
    +
    +
    +

    + Child component +

    +

    + Child component +

    +

    + Child component +

    +
    +
    +
    +`; + +exports[`Excerpt Content Node Node builds the expected component 1`] = ` +
    +
    +

    + Child component +

    +

    + Child component +

    +

    + Child component +

    +
    +
    +
    +`; + +exports[`Excerpt Content Node Node builds the expected component with non-null body style 1`] = ` +
    +
    +

    + Child component +

    +

    + Child component +

    +

    + Child component +

    +
    +
    +
    +`; + +exports[`Excerpt Content Node Node builds the expected component with non-standard body style 1`] = ` +
    +
    +

    + Child component +

    +

    + Child component +

    +

    + Child component +

    +
    +
    +
    +`; diff --git a/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.js b/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.js index 814507df12..543ddcb630 100644 --- a/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.js +++ b/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.js @@ -29,6 +29,7 @@ const getEdgeOptionsForBodyStyle = bodyStyle => { } const ExcerptContent = props => { + // console.log('props: ', props) // const onChangeEdge = (edge, edgeType) => { // const [parent, parentPath] = Editor.parent( // props.editor, @@ -50,6 +51,7 @@ const ExcerptContent = props => { // I was moving these methods over here, and then maybe I can no longer have to duplicate // properties and just use properties on this one, Excerpt Content?! const onChangePreset = presetValue => { + // console.log('changing preset in excerpt-content to ', presetValue) const [, parentPath] = Editor.parent( props.editor, ReactEditor.findPath(props.editor, props.element) @@ -68,6 +70,7 @@ const ExcerptContent = props => { } const onChangeContentValue = (contentValueName, value) => { + // console.log('changing content value', contentValueName,' in excerpt-content to', value) const [, parentPath] = Editor.parent( props.editor, ReactEditor.findPath(props.editor, props.element) @@ -84,6 +87,9 @@ const ExcerptContent = props => { const shouldShowEdgeControls = props.selected && Range.isCollapsed(props.editor.selection) + // console.log('selected: ', props.selected) + // console.log('isCollapsed: ', Range.isCollapsed(props.editor.selection)) + return (
    {shouldShowEdgeControls ? ( diff --git a/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.test.js b/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.test.js new file mode 100644 index 0000000000..29050e043c --- /dev/null +++ b/packages/obonode/obojobo-chunks-excerpt/components/excerpt-content/editor-component.test.js @@ -0,0 +1,301 @@ +import ExcerptContent from './editor-component' +import renderer from 'react-test-renderer' +import getPresetProps from '../../get-preset-props' +import { mount } from 'enzyme' +import { Editor, Range, Transforms } from 'slate' + +import React from 'react' + +jest.mock('slate') +jest.mock('slate-react', () => ({ + useSelected: jest.fn(), + useEditor: jest.fn(), + ReactEditor: { + findPath: jest.fn() + } +})) + +import { useEditor, useSelected, ReactEditor } from 'slate-react' + +describe('Excerpt Content Node', () => { + const Child = () =>

    Child component

    + + const content = { + bodyStyle: 'none', + width: 'medium', + font: 'serif', + lineHeight: 'moderate', + fontSize: 'smaller', + topEdge: 'normal', + bottomEdge: 'normal', + effect: false + } + + const editor = { + selection: [0] + } + + beforeEach(() => { + useEditor.mockImplementation(() => editor) + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + test('Node builds the expected component', () => { + const element = { + content + } + + useSelected.mockImplementation(() => false) + + const component = renderer.create( + + + + + + ) + + const tree = component.toJSON() + + expect(tree).toMatchSnapshot() + }) + + test('Node builds component when selected and collapsed', () => { + const element = { + content + } + + useSelected.mockImplementation(() => true) + + jest.spyOn(Range, 'isCollapsed').mockImplementation(() => true) + + const component = renderer.create( + + + + + + ) + + const tree = component.toJSON() + + expect(tree).toMatchSnapshot() + }) + + test('Node builds the expected component with non-null body style', () => { + const element = { + content: { + ...content, + bodyStyle: 'filled-box' + } + } + + useSelected.mockImplementation(() => false) + + const component = renderer.create( + + + + + + ) + + const tree = component.toJSON() + + expect(tree).toMatchSnapshot() + }) + + test('Node builds the expected component with non-standard body style', () => { + const element = { + content: { + ...content, + bodyStyle: 'new' + } + } + + useSelected.mockImplementation(() => false) + + const component = renderer.create( + + + + + + ) + + const tree = component.toJSON() + + expect(tree).toMatchSnapshot() + }) + + test('Node handles preset change', () => { + const element = { + content + } + + useSelected.mockImplementation(() => true) + jest.spyOn(Editor, 'parent').mockImplementation(() => [0, [2]]) + jest.spyOn(ReactEditor, 'findPath').mockImplementation(() => [2, 1]) + const setNodesSpy = jest.spyOn(Transforms, 'setNodes').mockImplementation(() => {}) + + const component = mount( + + + + + + ) + + // Button at index 2 corresponds to Simple Filled + component + .find('button') + .at(2) + .simulate('click') + + const newContent = { + ...content, + ...getPresetProps('simple-filled'), + preset: 'simple-filled' + } + + expect(setNodesSpy).toHaveBeenCalledTimes(2) + expect(setNodesSpy).toHaveBeenCalledWith(editor, { content: { ...newContent } }, { at: [2, 1] }) + expect(setNodesSpy).toHaveBeenCalledWith(editor, { content: { ...newContent } }, { at: [2] }) + }) + + test('Node handles top edge change', () => { + const element = { + content: { + ...content, + bodyStyle: 'filled-box' + } + } + + useSelected.mockImplementation(() => true) + jest.spyOn(Editor, 'parent').mockImplementation(() => [0, [2]]) + jest.spyOn(ReactEditor, 'findPath').mockImplementation(() => [2, 1]) + const setNodesSpy = jest.spyOn(Transforms, 'setNodes').mockImplementation(() => {}) + jest.spyOn(Range, 'isCollapsed').mockImplementation(() => true) + + const component = mount( + + + + + + ) + + component + .find('label') + .at(1) + .find('input') + .at(0) + .simulate('change', { + target: { value: 'fade' } + }) + + const newContent = { + ...element.content, + ['topEdge']: 'fade' + } + + expect(setNodesSpy).toHaveBeenCalledTimes(2) + expect(setNodesSpy).toHaveBeenCalledWith(editor, { content: { ...newContent } }, { at: [2, 1] }) + expect(setNodesSpy).toHaveBeenCalledWith(editor, { content: { ...newContent } }, { at: [2] }) + }) + + test('Node handles bottom edge change', () => { + const element = { + content: { + ...content, + bodyStyle: 'filled-box' + } + } + + useSelected.mockImplementation(() => true) + jest.spyOn(Editor, 'parent').mockImplementation(() => [0, [2]]) + jest.spyOn(ReactEditor, 'findPath').mockImplementation(() => [2, 1]) + const setNodesSpy = jest.spyOn(Transforms, 'setNodes').mockImplementation(() => {}) + jest.spyOn(Range, 'isCollapsed').mockImplementation(() => true) + + const component = mount( + + + + + + ) + + component + .find('label') + .at(4) + .find('input') + .at(0) + .simulate('change', { + target: { value: 'fade' } + }) + + const newContent = { + ...element.content, + ['bottomEdge']: 'fade' + } + + expect(setNodesSpy).toHaveBeenCalledTimes(2) + expect(setNodesSpy).toHaveBeenCalledWith(editor, { content: { ...newContent } }, { at: [2, 1] }) + expect(setNodesSpy).toHaveBeenCalledWith(editor, { content: { ...newContent } }, { at: [2] }) + }) + + test('Node handles excerpt edit', () => { + const element = { + content + } + + useSelected.mockImplementation(() => true) + jest.spyOn(Editor, 'parent').mockImplementation(() => [0, [2]]) + jest.spyOn(ReactEditor, 'findPath').mockImplementation(() => [2, 1]) + const setNodesSpy = jest.spyOn(Transforms, 'setNodes').mockImplementation(() => {}) + jest.spyOn(Range, 'isCollapsed').mockImplementation(() => true) + + const component = mount( + + + + + + ) + + // Open advanced options box + component + .find('Button') + .at(0) + .find('button') + .at(0) + .simulate('click') + + // console.log(component.find('.more-options').at(0).find('.radio-icons').at(0).find('input').at(0).debug()) + + // Select 'large' width option + component + .find('.more-options') + .at(0) + .find('.radio-icons') + .at(0) + .find('input') + .at(0) + .simulate('change', { + target: { value: 'large' } + }) + + const newContent = { + ...element.content, + ['width']: 'large' + } + + expect(setNodesSpy).toHaveBeenCalledTimes(2) + expect(setNodesSpy).toHaveBeenCalledWith(editor, { content: { ...newContent } }, { at: [2, 1] }) + expect(setNodesSpy).toHaveBeenCalledWith(editor, { content: { ...newContent } }, { at: [2] }) + }) +}) diff --git a/packages/obonode/obojobo-chunks-excerpt/components/excerpt-edit-controls.js b/packages/obonode/obojobo-chunks-excerpt/components/excerpt-edit-controls.js index 1112adabd0..06264819dd 100644 --- a/packages/obonode/obojobo-chunks-excerpt/components/excerpt-edit-controls.js +++ b/packages/obonode/obojobo-chunks-excerpt/components/excerpt-edit-controls.js @@ -3,7 +3,11 @@ import React, { useState } from 'react' import iconFontSizeSmall from '../icon-font-size-small.svg' import iconFontSizeMedium from '../icon-font-size-medium.svg' import iconFontSizeLarge from '../icon-font-size-large.svg' -import iconLineHeightCompact from '../icon-line-height-compact.svg' + +// This type of import results in an 'invalid prop `src` for img' error +// import iconLineHeightCompact from '../icon-line-height-compact.svg' + +const iconLineHeightCompact = require('../icon-line-height-compact.svg') import iconLineHeightModerate from '../icon-line-height-moderate.svg' import iconLineHeightGenerous from '../icon-line-height-generous.svg' import iconWidthLarge from '../icon-width-large.svg' @@ -120,6 +124,8 @@ const getEffectDescription = bodyStyle => { const ExcerptEditControls = ({ content, onChangeProp, onChangePreset }) => { const [isShowingMoreOptions, setIsShowingMoreOptions] = useState(false) + const effectAvailable = isEffectAvailable(content.bodyStyle) + return (
    { }, { label: 'medium', icon: }, @@ -238,7 +244,7 @@ const ExcerptEditControls = ({ content, onChangeProp, onChangePreset }) => { }, { label: 'regular', icon: }, @@ -254,7 +260,7 @@ const ExcerptEditControls = ({ content, onChangeProp, onChangePreset }) => { }, { label: 'moderate', icon: }, @@ -266,10 +272,10 @@ const ExcerptEditControls = ({ content, onChangeProp, onChangePreset }) => {