dataRow.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. /*
  2. * Copyright (C) 2014 - present Instructure, Inc.
  3. *
  4. * This file is part of Canvas.
  5. *
  6. * Canvas is free software: you can redistribute it and/or modify it under
  7. * the terms of the GNU Affero General Public License as published by the Free
  8. * Software Foundation, version 3 of the License.
  9. *
  10. * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
  11. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  13. * details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License along
  16. * with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. import React from 'react'
  19. import PropTypes from 'prop-types'
  20. import I18n from 'i18n!external_tools'
  21. import numberHelper from 'jsx/shared/helpers/numberHelper'
  22. const { bool, func, number } = PropTypes;
  23. var DataRow = React.createClass({
  24. propTypes: {
  25. onRowMinScoreChange: func.isRequired,
  26. uniqueId: number.isRequired,
  27. round: func.isRequired,
  28. editing: bool.isRequired,
  29. },
  30. getInitialState () {
  31. return { showBottomBorder: false };
  32. },
  33. componentWillReceiveProps () {
  34. this.setState({ showBottomBorder: false });
  35. },
  36. getRowData () {
  37. var rowData = {name: this.props.row[0], minScore: this.props.row[1], maxScore: null};
  38. rowData.maxScore = this.props.uniqueId === 0 ? 100 : this.props.siblingRow[1];
  39. return rowData;
  40. },
  41. hideBottomBorder () {
  42. this.setState({showBottomBorder: false});
  43. },
  44. showBottomBorder () {
  45. this.setState({showBottomBorder: true});
  46. },
  47. triggerRowNameChange (event) {
  48. this.props.onRowNameChange(this.props.uniqueId, event.target.value);
  49. },
  50. triggerRowMinScoreBlur () {
  51. if (this.state.minScoreInput == null) return;
  52. const inputVal = numberHelper.parse(this.state.minScoreInput);
  53. if (!isNaN(inputVal) && inputVal >= 0 && inputVal <= 100) {
  54. this.props.onRowMinScoreChange(this.props.uniqueId, String(inputVal));
  55. }
  56. this.setState({ minScoreInput: null });
  57. },
  58. triggerRowMinScoreChange (event) {
  59. this.setState({ minScoreInput: event.target.value });
  60. },
  61. triggerDeleteRow (event) {
  62. event.preventDefault();
  63. return this.props.onDeleteRow(this.props.uniqueId);
  64. },
  65. triggerInsertRow (event) {
  66. event.preventDefault();
  67. return this.props.onInsertRow(this.props.uniqueId);
  68. },
  69. renderInsertRowButton () {
  70. return (
  71. <button className="Button Button--icon-action insert_row_button"
  72. onMouseEnter={this.showBottomBorder} onFocus={this.showBottomBorder}
  73. onBlur={this.hideBottomBorder} onMouseLeave={this.hideBottomBorder}
  74. onClick={this.triggerInsertRow} type="button">
  75. <span className="screenreader-only">{I18n.t("Insert row below")}</span>
  76. <i className="icon-add"/>
  77. </button>);
  78. },
  79. renderMaxScore () {
  80. const maxScore = this.props.round(this.getRowData().maxScore);
  81. return (maxScore === 100 ? '' : '< ') + I18n.n(maxScore);
  82. },
  83. renderMinScore () {
  84. let minScore = this.getRowData().minScore;
  85. if (!this.props.editing) {
  86. minScore = this.props.round(minScore);
  87. } else if (this.state.minScoreInput != null) {
  88. return this.state.minScoreInput;
  89. }
  90. return I18n.n(minScore);
  91. },
  92. renderDeleteRowButton () {
  93. if(this.props.onlyDataRowRemaining) return null;
  94. return(
  95. <button ref="deleteButton" className="Button Button--icon-action delete_row_button"
  96. onClick={this.triggerDeleteRow} type="button">
  97. <span className="screenreader-only">{I18n.t("Remove row")}</span>
  98. <i className="icon-end"/>
  99. </button>
  100. );
  101. },
  102. renderViewMode () {
  103. return (
  104. <tr className="grading_standard_row react_grading_standard_row" ref="viewContainer">
  105. <td className="insert_row_icon_container"/>
  106. <td className="row_name_container">
  107. <div className="name" ref="name">
  108. {this.getRowData().name}
  109. </div>
  110. </td>
  111. <td className="row_cell max_score_cell" ariaLabel={I18n.t('Upper limit of range')} >
  112. <div>
  113. <span className="max_score" ref="maxScore" title="Upper limit of range">
  114. {this.renderMaxScore() + "%"}
  115. </span>
  116. </div>
  117. </td>
  118. <td className="row_cell">
  119. <div>
  120. <span className="range_to" ref="minScore">{I18n.t("to %{minScore}%", {minScore: this.renderMinScore()})}</span>
  121. <span className="min_score">
  122. </span>
  123. </div>
  124. </td>
  125. <td className="row_cell last_row_cell"/>
  126. </tr>
  127. );
  128. },
  129. renderEditMode () {
  130. return (
  131. <tr className={this.state.showBottomBorder ?
  132. "grading_standard_row react_grading_standard_row border_below" :
  133. "grading_standard_row react_grading_standard_row"}
  134. ref="editContainer">
  135. <td className="insert_row_icon_container">
  136. {this.renderInsertRowButton()}
  137. </td>
  138. <td className="row_name_container">
  139. <div>
  140. <input type="text" ref="nameInput" onChange={this.triggerRowNameChange}
  141. className="standard_name" title={I18n.t('Range name')} ariaLabel={I18n.t('Range name')}
  142. name={"grading_standard[standard_data][scheme_" + this.props.uniqueId + "[name]"}
  143. value={this.getRowData().name}/>
  144. </div>
  145. </td>
  146. <td className="row_cell max_score_cell edit_max_score">
  147. <span className="edit_max_score">
  148. {this.renderMaxScore() + "%"}
  149. <span className="screenreader-only">{I18n.t("Upper limit of range")}</span>
  150. </span>
  151. </td>
  152. <td className="row_cell">
  153. <div>
  154. <span className="range_to" ariaHidden="true">{I18n.t("to ")}</span>
  155. <input
  156. type="text"
  157. className="standard_value"
  158. ref={(input) => { this.minScoreInput = input; }}
  159. onChange={this.triggerRowMinScoreChange}
  160. onBlur={this.triggerRowMinScoreBlur}
  161. title={I18n.t('Lower limit of range')}
  162. ariaLabel={I18n.t('Lower limit of range')}
  163. name={`grading_standard[standard_data][scheme_${this.props.uniqueId}][value]`}
  164. value={this.renderMinScore()}
  165. />
  166. <span ariaHidden="true"> % </span>
  167. </div>
  168. </td>
  169. <td className="row_cell last_row_cell">
  170. {this.renderDeleteRowButton()}
  171. </td>
  172. </tr>
  173. );
  174. },
  175. render () {
  176. return this.props.editing ? this.renderEditMode() : this.renderViewMode();
  177. }
  178. });
  179. export default DataRow