packages/issue-dashboard-widgets/widgets/youtrack-activities-widget/app/components/issue-card.js (267 lines of code) (raw):

import React from 'react'; import PropTypes from 'prop-types'; import {i18n} from 'hub-dashboard-addons/dist/localization'; import fecha from 'fecha'; import { ChevronDownIcon, ChevronRightIcon } from '@jetbrains/ring-ui/components/icon'; import filter from '../activities-filter'; import {loadIssue} from '../resources'; import IssueLink from './issue-link'; class IssueCard extends React.Component { static fieldColorToCss(color) { return { background: color.background, color: color.foreground }; } static getValuableIssueFields(issue) { return (issue.fields || []).filter( field => IssueCard.toArray(field.value || []).length > 0 ).filter( field => { const valueType = field.projectCustomField && field.projectCustomField.field && field.projectCustomField.field.fieldType && field.projectCustomField.field.fieldType.valueType; return valueType !== 'text'; } ); } static getName(field) { return field.localizedName || field.name; } static getDatePresentation(timestamp, dateFormats, withTime) { return fecha.format( timestamp, withTime ? dateFormats.dateTimePattern : dateFormats.datePattern ); } static getValuePresentation(issueField, dateFormats) { const field = issueField.projectCustomField && issueField.projectCustomField.field; const fieldType = (field && field.fieldType && field.fieldType.valueType) || ''; return IssueCard.toArray(issueField.value || []).map(value => { if (fieldType.indexOf('date') > -1) { return IssueCard.getDatePresentation( value, dateFormats, fieldType.indexOf('time') > -1 ); } return IssueCard.getName(value) || value.presentation || value.minutes || value.name || value.login || value; }).join(', '); } static toArray = value => (Array.isArray(value) ? value : [value]); static getFirstLetter = value => (IssueCard.getName(value) || 'c')[0].toUpperCase(); static isColoredValue = value => value.color && value.color.id > 0; static getColoredSquareModel(issue) { const makeColorFieldPresentationObject = issueField => { const coloredValue = IssueCard.toArray(issueField.value).filter( IssueCard.isColoredValue )[0]; if (!coloredValue) { return null; } const fieldName = IssueCard.getName( issueField.projectCustomField.field || {} ); return { style: IssueCard.fieldColorToCss(coloredValue.color), letter: IssueCard.getFirstLetter(coloredValue), title: `${fieldName}: ${IssueCard.getName(coloredValue)}`, issueField }; }; const bundleFields = (issue.fields || []).filter( issueField => !!issueField.projectCustomField.bundle ); const priorityField = bundleFields.filter( issueField => { const field = issueField.projectCustomField.field || {}; return (field.name || '').toLowerCase() === 'priority'; } )[0]; if (priorityField) { if (priorityField.value) { return makeColorFieldPresentationObject(priorityField); } return null; } const fieldWithColoredValues = (issue.fields || []).filter( field => IssueCard.toArray(field.value || []).some(IssueCard.isColoredValue) )[0]; if (!fieldWithColoredValues) { return null; } return makeColorFieldPresentationObject(fieldWithColoredValues); } static onOpenIssue = evt => evt.stopPropagation(); static propTypes = { issue: PropTypes.object, removed: PropTypes.bool, homeUrl: PropTypes.string, dateFormats: PropTypes.object, showMore: PropTypes.bool }; static defaultProps = { dateFormats: { datePattern: 'YYYY-MM-DD', dateTimePattern: 'YYYY-MM-DD\'T\'HH:mm:ss' } }; constructor(props) { super(props); this.state = { expanded: false, issue: this.props.issue, coloredSquare: null, //IssueCard.getColoredSquareModel(issue), valuableFields: null //IssueCard.getValuableIssueFields(issue) }; this.getFieldsAndExpand = this.getFieldsAndExpand.bind(this); } fetchYouTrack = async (url, params) => { const {dashboardApi, youTrackId} = filter; return await dashboardApi.fetch(youTrackId, url, params); }; // static getDerivedStateFromProps(props) { // const {issue, expanded} = props; // return { // issue, // expanded, // coloredSquare: IssueCard.getColoredSquareModel(issue), // valuableFields: IssueCard.getValuableIssueFields(issue) // }; // } renderFieldValue(issueField) { const firstValue = IssueCard.toArray(issueField.value)[0]; return ( <div className="aw__issue-card__panel__field-value"> {IssueCard.getValuePresentation(issueField, this.props.dateFormats)} { firstValue.avatarUrl && ( <img className="aw__issue-card__panel__field-avatar" src={firstValue.avatarUrl} /> ) } { IssueCard.isColoredValue(firstValue) && ( <span className="aw__issue-card__panel__field-color aw__issue-card__panel__colored-field" style={IssueCard.fieldColorToCss(firstValue.color)} > {IssueCard.getFirstLetter(firstValue)} </span> ) } </div> ); } renderFields(issueFields, fixed) { const fixClassName = fixed ? 'aw__issue-card__panel__fields-fix' : ''; return ( <div className={`aw__issue-card__panel__fields ${fixClassName}`}> { issueFields.map(issueField => ( <div key={`field-line-${issueField.id}`} className="aw__issue-card__panel__field-row" > <div className="aw__issue-card__panel__field"> <div className="aw__issue-card__panel__field-title"> {IssueCard.getName(issueField.projectCustomField.field)} </div> {this.renderFieldValue(issueField)} </div> </div> )) } </div> ); } getFieldsAndExpand = async () => { const initialIssue = this.props.issue; if (this.state.valuableFields && this.state.valuableFields.length) { this.setState({expanded: true}); } else { const issue = await loadIssue(this.fetchYouTrack, initialIssue.id); this.setState( { issue, expanded: true, valuableFields: IssueCard.getValuableIssueFields(issue) } ); } }; closeFields = async () => { this.setState({expanded: false}); }; getOnClick(expanded) { return expanded ? this.closeFields : this.getFieldsAndExpand; } renderChevron(expanded, color) { return expanded ? ( <ChevronDownIcon size={ChevronDownIcon.Size.Size14} color={color} onClick={this.getOnClick(expanded)} /> ) : ( <ChevronRightIcon size={ChevronRightIcon.Size.Size14} color={color} onClick={this.getOnClick(expanded)} /> ); } render() { const { issue, valuableFields, expanded } = this.state; const {showMore} = this.props; return ( <div className="aw__issue-card"> <div className="aw__issue-card__header" key={issue.id} > { !showMore && ( <React.Fragment> <div className="aw__issue-card__header__toggle"> {this.renderChevron(expanded, ChevronDownIcon.Color.GRAY)} </div> <div className="aw__issue-card__header__link"> <IssueLink issue={issue}/> </div> </React.Fragment> ) } { showMore && ( <div className="aw__issue-card__header__toggle"> <a className="aw__toggle" onClick={this.getOnClick(expanded)} > {this.renderChevron(expanded, ChevronDownIcon.Color.BLUE)} <span className="aw__toggle__text"> {i18n('Show more')} </span> </a> </div> ) } </div> { expanded && ( <div className="aw__issue-card__panel__issue-expanded-block" data-test="issue-line-expanded-block" > {this.renderFields(valuableFields)} </div> ) } </div> ); } } export default IssueCard;