Codeminer42/cm42-central

View on GitHub
app/assets/javascripts/components/story/CollapsedStory/index.jsx

Summary

Maintainability
A
0 mins
Test Coverage
import React, { useMemo } from 'react';
import { connect } from 'react-redux';
import { Draggable } from 'react-beautiful-dnd';
import classname from 'classnames';
import PropTypes from 'prop-types';
import { updateCollapsedStory, highlight } from '../../../actions/story';
import StoryPopover from '../StoryPopover';
import StoryDescriptionIcon from '../StoryDescriptionIcon';
import CollapsedStoryEstimate from './CollapsedStoryEstimate';
import CollapsedStoryStateActions from './CollapsedStoryStateActions';
import CollapsedStoryInfo from './CollapsedStoryInfo';
import StoryIcon from '../StoryIcon';
import MDSpinner from 'react-md-spinner';
import * as Story from '../../../models/beta/story';
import { status, columns } from '../../../libs/beta/constants';

import CollapsedStoryFocusButon from './CollapsedStoryFocusButton';
import StoryPropTypes from '../../shapes/story';
import { getStories, getStoriesWithScope } from '../../../selectors/stories';
import { getProject } from '../../../selectors/project';

const storyClassName = (
  story,
  additionalClassname = '',
  isDragging,
  isDragDisabled
) => {
  const isStoryNotEstimated = Story.isStoryNotEstimated(
    story.storyType,
    story.estimate
  );
  const isRelease = Story.isRelease(story);
  return classname(
    'Story Story--collapsed',
    {
      'Story--release': isRelease,
      'Story--unestimated': isStoryNotEstimated,
      'Story--estimated': !isStoryNotEstimated,
      'Story--isDragging': isDragging,
      'Story--cantBeMoved': story.loading || isDragDisabled,
    },
    additionalClassname
  );
};

export const Container = ({
  onToggle,
  story,
  updateCollapsedStory,
  project,
  className,
  title,
  highlight,
  isHighlightable,
  isDragging,
  provided,
  isDragDisabled,
  onLabelClick,
}) => (
  <div
    className={storyClassName(story, className, isDragging, isDragDisabled)}
    onClick={!story.loading ? onToggle : () => {}}
    title={title}
    {...provided.draggableProps}
    {...provided.dragHandleProps}
    ref={provided.innerRef}
  >
    <StoryPopover story={story}>
      <div className="Story__icons-block">
        <StoryIcon storyType={story.storyType} />
        <CollapsedStoryEstimate estimate={story.estimate} />
        <StoryDescriptionIcon description={story.description} />
      </div>
    </StoryPopover>

    <CollapsedStoryInfo story={story} onLabelClick={onLabelClick} />

    {story.loading ? (
      <MDSpinner size={20} singleColor={'#333'} />
    ) : (
      <CollapsedStoryStateActions
        story={story}
        onUpdate={newAttributes =>
          updateCollapsedStory(story.id, project.id, newAttributes)
        }
      />
    )}
    {isHighlightable && (
      <CollapsedStoryFocusButon onClick={() => highlight(story.id)} />
    )}
  </div>
);

export const CollapsedStory = ({ index, sprintIndex, columnId, ...props }) => {
  const { story } = { ...props };

  const isDragDisabled = useMemo(
    () =>
      story.state === status.ACCEPTED ||
      columnId === columns.EPIC ||
      columnId === columns.SEARCH ||
      story.loading,
    [story, columnId]
  );

  return (
    <Draggable
      draggableId={JSON.stringify({ id: story.id.toString(), sprintIndex })}
      index={index}
      isDragDisabled={isDragDisabled}
    >
      {(provided, snapshot) => (
        <Container
          {...props}
          provided={provided}
          isDragging={snapshot.isDragging}
          isDragDisabled={isDragDisabled}
        />
      )}
    </Draggable>
  );
};

CollapsedStory.propTypes = {
  story: StoryPropTypes,
  onToggle: PropTypes.func.isRequired,
  title: PropTypes.string,
  className: PropTypes.string,
  from: PropTypes.string,
  highlight: PropTypes.func,
  index: PropTypes.number,
  sprintIndex: PropTypes.number,
  columnId: PropTypes.string,
  onLabelClick: PropTypes.func.isRequired,
};

const mapStateToProps = (state, props) => ({
  project: getProject(state),
  stories: getStories(state),
  isHighlightable: Story.haveHighlightButton(
    getStoriesWithScope(state),
    props.story,
    props.from
  ),
});

const mapDispatchToProps = { updateCollapsedStory, highlight };

export default connect(mapStateToProps, mapDispatchToProps)(CollapsedStory);