diff --git a/src/components/Card.jsx b/src/components/Card.jsx index bb2864e..7dce85b 100644 --- a/src/components/Card.jsx +++ b/src/components/Card.jsx @@ -1,27 +1,35 @@ -import '../scss/main.scss'; import copy from '../js/data/copy.json'; import {isNotNullNorUndefined} from '../js/data/utilities'; import React from 'react'; +import Spinner from './presentational/Spinner'; +import CardTimestamp from './presentational/CardTimestamp'; +import CardLocation from './presentational/CardLocation'; +import CardCaret from './presentational/CardCaret'; +import CardTags from './presentational/CardTags'; +import CardSummary from './presentational/CardSummary'; +import CardSource from './presentational/CardSource'; +import CardCategory from './presentational/CardCategory'; +import CardNarrative from './presentational/CardNarrative'; + class Card extends React.Component { constructor(props) { super(props); this.state = { - isFolded: true + isHighlighted: false }; - - this.toggle = this.toggle.bind(this); } toggle() { - if (this.state.isFolded) { - this.props.highlight(this.props.event); - } else { - this.props.highlight(); - } this.setState({ - isFolded: !this.state.isFolded + isHighlighted: !this.state.isHighlighted + }, () => { + if (this.state.isHighlighted) { + this.props.highlight(this.props.event); + } else { + this.props.highlight(); + } }); } @@ -31,192 +39,148 @@ class Card extends React.Component { return 'other'; } - renderWarning() { - const warning_lang = copy[this.props.language].cardstack.warning; - - if (this.props.event.tags) { - const tagsArray = this.props.event.tags.split(","); - /* TODO: This needs to be generalized */ - if (tagsArray.some(tag => { - return (tag.name === 'contradicción' || - tag.name === 'declaración con sospecha de tortura') - })) { - return (
{warning_lang}
); - } - } + makeTimelabel(timestamp) { + if (timestamp === null) return null; + const parsedTimestamp = this.props.tools.parser(timestamp); + const timelabel = this.props.tools.formatterWithYear(parsedTimestamp); + return timelabel; } renderCategory() { - const category_lang = copy[this.props.language].cardstack.category; - + const categoryTitle = copy[this.props.language].cardstack.category; const colorType = this.getCategoryColorClass(this.props.event.category); const categoryLabel = this.props.getCategoryLabel(this.props.event.category); - return (
-

{category_lang}

-

{categoryLabel}

-
); + return ( + + ); } renderSummary() { - const summary = copy[this.props.language].cardstack.description; - const desc = this.props.event.description; - const description = (this.state.isFolded) ? `${desc.substring(0, 40)}...` : desc; - return (
-

{summary}

-

{description}

-
); + return ( + + ) } renderTags() { - const people_lang = copy[this.props.language].cardstack.people; - const peopleTags = []; //this.props.event.tags.filter(tag => tag.type === 'people'); - - return (
-

{people_lang}

-

{ - peopleTags.map((tag, idx) => { - return ( - {tag.name} - { - (idx < peopleTags.length - 1) - ? ',' - : '' - } - ); - }) - }

-
); + return ( + + ) } - // NB: is this function for a future feature? Should also be internaionalized. renderLocation() { - const location_lang = copy[this.props.language].cardstack.location; - if (isNotNullNorUndefined(this.props.event.location)) { - return (

-

{location_lang}

-

{this.props.event.location}

-

); - } else { - return (

-

{location_lang}

-

Sin localización conocida.

-

); - } + return ( + + ) } renderSource() { - const source_lang = copy[this.props.language].cardstack.source; - return (
-

{source_lang}

-

{this.props.event.source}

-
); - } - - getTimeLabel(){ - const timestamp = this.props.tools.parser(this.props.event.timestamp); - const timelabel = this.props.tools.formatterWithYear(timestamp); - return timelabel; + return ( + + ) } // NB: should be internaionalized. renderTimestamp() { - const daytime_lang = copy[this.props.language].cardstack.timestamp; - const estimated_lang = copy[this.props.language].cardstack.estimated; - - if (isNotNullNorUndefined(this.props.event.timestamp)) { - const timelabel = this.getTimeLabel(); - return (
-

{daytime_lang}

- {timelabel} -
); - } else { - return (
-

{daytime_lang}

- Hora no conocida -
); - } - } - - renderCardLink(event, direction) { - if (event !== null) { - const timelabel = this.getTimeLabel(); - return ( this.props.select([event])}> - {`${timelabel} - ${event.location}`} - ); - } - return (None); + return ( + this.makeTimelabel(timestamp)} + language={this.props.language} + timestamp={this.props.event.timestamp} + /> + ); } renderNarrative() { const links = this.props.getNarrativeLinks(this.props.event); if (links !== null) { - return (
-

Connected events

-

Next: {this.renderCardLink(links.next, 'next')}

-

Previous: {this.renderCardLink(links.prev, 'prev')}

-
); + + return ( + this.props.select([event])} + makeTimelabel={(timestamp) => this.makeTimelabel(timestamp)} + next={links.next} + prev={links.prev} + /> + ) } } - renderSpinner() { - return (
-
-
-
); + renderLoadingCard() { + return ( +
  • +
    + +
    +
  • + ); } renderHeader() { - return (
    - {this.renderWarning()} - {this.renderCategory()} - {this.renderTimestamp()} - {this.renderSummary()} -
    ); + return ( +
    + {this.renderCategory()} + {this.renderTimestamp()} + {this.renderSummary()} +
    + ); } renderContent() { - if (this.state.isFolded) { - return (
    ); - } else if (this.props.isFetchingEvents) { - return (
    - {this.renderSpinner()} -
    ); + if (!this.state.isHighlighted) { + return ( +
    + ); } else { - return (
    - {this.renderLocation()} - {this.renderTags()} - {this.renderSource()} - {this.renderNarrative()} -
    ); + return ( +
    + {this.renderLocation()} + {this.renderTags()} + {this.renderSource()} + {this.renderNarrative()} +
    + ); } } - renderArrow() { - let classes = (this.state.isFolded) - ? 'arrow-down folded' - : 'arrow-down'; - return (
    this.toggle()}> -

    - -

    -
    ); + renderCaret() { + return ( + this.toggle()} + isHighlighted={this.state.isHighlighted} + /> + ) } render() { if (this.props.isLoading) { - return (
  • -
    - {this.renderSpinner()} -
    -
  • ); + return this.renderLoadingCard(); } else { - return (
  • - {this.renderHeader()} - {this.renderContent()} - {this.renderArrow()} -
  • ); + return ( +
  • + {this.renderHeader()} + {this.renderContent()} + {this.renderCaret()} +
  • + ); } } } diff --git a/src/components/CardStack.jsx b/src/components/CardStack.jsx index 30afba2..b616132 100644 --- a/src/components/CardStack.jsx +++ b/src/components/CardStack.jsx @@ -1,4 +1,3 @@ -import '../scss/main.scss'; import React from 'react'; import Card from './Card.jsx'; import copy from '../js/data/copy.json'; @@ -20,17 +19,17 @@ class CardStack extends React.Component { return ( ); }); @@ -50,30 +49,34 @@ class CardStack extends React.Component { return ''; } + renderIsLoading() { + return ( +
    +
    this.props.toggle('TOGGLE_CARDSTACK')} + > + +

    {copy[this.props.language].loading}

    +
    +
    +
      + +
    +
    +
    + ) + } + render() { const header_lang = copy[this.props.language].cardstack.header; if (this.props.isFetchingEvents) { - return ( -
    -
    this.props.toggle('TOGGLE_CARDSTACK')} - > - -

    {copy[this.props.language].loading}

    -
    -
    -
      - -
    -
    -
    - ); + return this.renderIsLoading(); } else if (this.props.selected.length > 0) { return (
    diff --git a/src/components/Dashboard.jsx b/src/components/Dashboard.jsx index 1a46d0c..123c9c5 100644 --- a/src/components/Dashboard.jsx +++ b/src/components/Dashboard.jsx @@ -1,4 +1,3 @@ -import '../scss/main.scss'; import React from 'react'; import { bindActionCreators } from 'redux'; diff --git a/src/components/TagFilter.jsx b/src/components/TagFilter.jsx index dda91ac..cf68eb4 100644 --- a/src/components/TagFilter.jsx +++ b/src/components/TagFilter.jsx @@ -1,6 +1,5 @@ -import '../scss/main.scss'; import React from 'react'; -import Checkbox from './Checkbox.jsx'; +import Checkbox from './presentational/Checkbox.jsx'; class TagFilter extends React.Component { constructor(props) { diff --git a/src/components/TagListPanel.jsx b/src/components/TagListPanel.jsx index da5a6a7..4e45d52 100644 --- a/src/components/TagListPanel.jsx +++ b/src/components/TagListPanel.jsx @@ -1,6 +1,5 @@ -import '../scss/main.scss'; import React from 'react'; -import Checkbox from './Checkbox.jsx'; +import Checkbox from './presentational/Checkbox.jsx'; class TagListPanel extends React.Component { diff --git a/src/components/Timeline.jsx b/src/components/Timeline.jsx index e388ad0..9a2a939 100644 --- a/src/components/Timeline.jsx +++ b/src/components/Timeline.jsx @@ -1,4 +1,3 @@ -import '../scss/main.scss'; import copy from '../js/data/copy.json'; import React from 'react'; import TimelineLogic from '../js/timeline/timeline.js'; @@ -77,6 +76,7 @@ class Timeline extends React.Component { let classes = `timeline-wrapper ${(this.state.isFolded) ? ' folded' : ''}`; const date0 = this.props.tools.formatterWithYear(this.props.timerange[0]); const date1 = this.props.tools.formatterWithYear(this.props.timerange[1]); + return (
    diff --git a/src/components/Toolbar.jsx b/src/components/Toolbar.jsx index 34c9a38..6015bc4 100644 --- a/src/components/Toolbar.jsx +++ b/src/components/Toolbar.jsx @@ -1,4 +1,4 @@ -import '../scss/main.scss'; + import React from 'react'; import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'; import Search from './Search.jsx'; diff --git a/src/components/Viewport.jsx b/src/components/Viewport.jsx index 3292a94..9c10de8 100644 --- a/src/components/Viewport.jsx +++ b/src/components/Viewport.jsx @@ -1,4 +1,3 @@ -import '../scss/main.scss'; import React from 'react'; import Map from '../js/map/map.js'; import { areEqual } from '../js/data/utilities.js'; diff --git a/src/components/presentational/CardCaret.js b/src/components/presentational/CardCaret.js new file mode 100644 index 0000000..36994b3 --- /dev/null +++ b/src/components/presentational/CardCaret.js @@ -0,0 +1,18 @@ +import React from 'react'; + +const CardCaret = ({ isHighlighted, toggle }) => { + + let classes = (isHighlighted) + ? 'arrow-down' + : 'arrow-down folded'; + + return ( +
    +

    + +

    +
    + ); +} + +export default CardCaret; diff --git a/src/components/presentational/CardCategory.js b/src/components/presentational/CardCategory.js new file mode 100644 index 0000000..a106112 --- /dev/null +++ b/src/components/presentational/CardCategory.js @@ -0,0 +1,13 @@ +import React from 'react'; + +const CardCategory = ({ categoryTitle, categoryLabel, colorType }) => ( +
    +

    {categoryTitle}

    +

    + + {categoryLabel} +

    +
    +); + +export default CardCategory; diff --git a/src/components/presentational/CardLocation.js b/src/components/presentational/CardLocation.js new file mode 100644 index 0000000..7dd26c0 --- /dev/null +++ b/src/components/presentational/CardLocation.js @@ -0,0 +1,26 @@ +import React from 'react'; + +import copy from '../../js/data/copy.json'; +import {isNotNullNorUndefined} from '../../js/data/utilities'; + +const CardLocation = ({ language, location }) => { + + const location_lang = copy[language].cardstack.location; + if (isNotNullNorUndefined(location)) { + return ( +

    +

    {location_lang}

    +

    {location}

    +

    + ); + } else { + return ( +

    +

    {location_lang}

    +

    Sin localización conocida.

    +

    + ); + } +} + +export default CardLocation; diff --git a/src/components/presentational/CardNarrative.js b/src/components/presentational/CardNarrative.js new file mode 100644 index 0000000..9610215 --- /dev/null +++ b/src/components/presentational/CardNarrative.js @@ -0,0 +1,13 @@ +import React from 'react'; + +import CardNarrativeLink from './CardNarrativeLink'; + +const CardNarrative = (props) => ( +
    +

    Connected events

    +

    Next:

    +

    Previous:

    +
    +); + +export default CardNarrative; diff --git a/src/components/presentational/CardNarrativeLink.js b/src/components/presentational/CardNarrativeLink.js new file mode 100644 index 0000000..febffc5 --- /dev/null +++ b/src/components/presentational/CardNarrativeLink.js @@ -0,0 +1,17 @@ +import React from 'react'; + +const CardNarrativeLink = ({ event, makeTimelabel, select }) => { + if (event !== null) { + const timelabel = makeTimelabel(event.timestamp); + + return ( + select(event)}> + {`${timelabel} - ${event.location}`} + + ); + } + + return (None); +} + +export default CardNarrativeLink; diff --git a/src/components/presentational/CardSource.js b/src/components/presentational/CardSource.js new file mode 100644 index 0000000..4597d49 --- /dev/null +++ b/src/components/presentational/CardSource.js @@ -0,0 +1,16 @@ +import React from 'react'; + +import copy from '../../js/data/copy.json'; + +const CardSource = ({ source, language }) => { + const source_lang = copy[language].cardstack.source; + + return ( +
    +

    {source_lang}

    +

    {source}

    +
    + ); +} + +export default CardSource; diff --git a/src/components/presentational/CardSummary.js b/src/components/presentational/CardSummary.js new file mode 100644 index 0000000..bb4f10f --- /dev/null +++ b/src/components/presentational/CardSummary.js @@ -0,0 +1,18 @@ +import React from 'react'; + +import copy from '../../js/data/copy.json'; + +const CardSummary = ({ language, description, isHighlighted }) => { + + const summary = copy[language].cardstack.description; + const descriptionText = (isHighlighted) ? description : `${description.substring(0, 40)}...`; + + return ( +
    +

    {summary}

    +

    {descriptionText}

    +
    + ); +} + +export default CardSummary; diff --git a/src/components/presentational/CardTags.js b/src/components/presentational/CardTags.js new file mode 100644 index 0000000..46088cb --- /dev/null +++ b/src/components/presentational/CardTags.js @@ -0,0 +1,29 @@ +import React from 'react'; + +import copy from '../../js/data/copy.json'; + +const CardTags = ({ tags, language }) => { + const label = copy[language].cardstack.people; + + return ( +
    +

    {label}

    +

    { + tags.map((tag, idx) => { + return ( + + {tag.name} + { + (idx < tags.length - 1) + ? ',' + : '' + } + + ); + }) + }

    +
    +); +} + +export default CardTags; diff --git a/src/components/presentational/CardTimestamp.js b/src/components/presentational/CardTimestamp.js new file mode 100644 index 0000000..d645e82 --- /dev/null +++ b/src/components/presentational/CardTimestamp.js @@ -0,0 +1,30 @@ +import React from 'react'; + +import copy from '../../js/data/copy.json'; +import {isNotNullNorUndefined} from '../../js/data/utilities'; + +const CardTimestamp = ({ makeTimelabel, language, timestamp }) => { + + const daytime_lang = copy[language].cardstack.timestamp; + const estimated_lang = copy[language].cardstack.estimated; + const unknown_lang = copy[language].cardstack.unknown_time; + + if (isNotNullNorUndefined(timestamp)) { + const timelabel = makeTimelabel(timestamp); + return ( +
    +

    {daytime_lang}

    + {timelabel} +
    + ); + } else { + return ( +
    +

    {daytime_lang}

    + {unknown_lang} +
    + ); + } +} + +export default CardTimestamp; diff --git a/src/components/Checkbox.jsx b/src/components/presentational/Checkbox.jsx similarity index 91% rename from src/components/Checkbox.jsx rename to src/components/presentational/Checkbox.jsx index 2bdd8dc..c57d409 100644 --- a/src/components/Checkbox.jsx +++ b/src/components/presentational/Checkbox.jsx @@ -1,4 +1,3 @@ -import '../scss/main.scss'; import React from 'react'; export default ({ label, isActive, onClickCheckbox }) => ( diff --git a/src/components/presentational/Spinner.js b/src/components/presentational/Spinner.js new file mode 100644 index 0000000..e3bad6d --- /dev/null +++ b/src/components/presentational/Spinner.js @@ -0,0 +1,12 @@ +import React from 'react'; + +const Spinner = ({}) => { + return ( +
    +
    +
    +
    + ) +} + +export default Spinner; diff --git a/src/js/data/copy.json b/src/js/data/copy.json index 2138326..66efb16 100644 --- a/src/js/data/copy.json +++ b/src/js/data/copy.json @@ -56,6 +56,7 @@ "cardstack": { "header": "eventos seleccionados", "unknown_location": "Localización desconocida", + "unknown_time": "Día y hora desconocida", "timestamp": "Día y hora", "estimated": "aproximado", "location": "Localización", @@ -129,6 +130,7 @@ "timestamp": "Day and time", "unknown_location": "Unknown location", "estimated": "estimated", + "unknown_time": "Unknown time", "location": "Localization", "incident_type": "Type of action", "description": "Summary of facts",