|
1 | | -import { ISettingRegistry } from '@jupyterlab/coreutils'; |
2 | | - |
3 | 1 | import * as React from 'react'; |
4 | 2 | import TextareaAutosize from 'react-textarea-autosize'; |
5 | 3 | import { classes } from 'typestyle'; |
6 | | - |
7 | 4 | import { |
8 | | - stagedCommitButtonDisabledStyle, |
9 | | - stagedCommitButtonReadyStyle, |
10 | | - stagedCommitButtonStyle, |
11 | | - stagedCommitMessageStyle, |
12 | | - stagedCommitStyle, |
13 | | - textInputStyle |
14 | | -} from '../style/BranchHeaderStyle'; |
| 5 | + commitFormClass, |
| 6 | + commitSummaryClass, |
| 7 | + commitDescriptionClass, |
| 8 | + commitButtonClass, |
| 9 | + commitButtonDisabledClass |
| 10 | +} from '../style/CommitBox'; |
15 | 11 |
|
| 12 | +/** |
| 13 | + * Interface describing component properties. |
| 14 | + */ |
16 | 15 | export interface ICommitBoxProps { |
| 16 | + /** |
| 17 | + * Boolean indicating whether files currently exist which have changes to commit. |
| 18 | + */ |
17 | 19 | hasFiles: boolean; |
18 | | - commitFunc: (message: string) => Promise<void>; |
19 | | - settings: ISettingRegistry.ISettings; |
| 20 | + |
| 21 | + /** |
| 22 | + * Callback to invoke in order to commit changes. |
| 23 | + * |
| 24 | + * @param msg - commit message |
| 25 | + * @returns a promise which commits changes |
| 26 | + */ |
| 27 | + onCommit: (msg: string) => Promise<void>; |
20 | 28 | } |
21 | 29 |
|
| 30 | +/** |
| 31 | + * Interface describing component state. |
| 32 | + */ |
22 | 33 | export interface ICommitBoxState { |
23 | 34 | /** |
24 | | - * Commit message |
| 35 | + * Commit message summary. |
25 | 36 | */ |
26 | | - value: string; |
| 37 | + summary: string; |
| 38 | + |
| 39 | + /** |
| 40 | + * Commit message description. |
| 41 | + */ |
| 42 | + description: string; |
27 | 43 | } |
28 | 44 |
|
| 45 | +/** |
| 46 | + * React component for entering a commit message. |
| 47 | + */ |
29 | 48 | export class CommitBox extends React.Component< |
30 | 49 | ICommitBoxProps, |
31 | 50 | ICommitBoxState |
32 | 51 | > { |
| 52 | + /** |
| 53 | + * Returns a React component for entering a commit message. |
| 54 | + * |
| 55 | + * @param props - component properties |
| 56 | + * @returns React component |
| 57 | + */ |
33 | 58 | constructor(props: ICommitBoxProps) { |
34 | 59 | super(props); |
35 | 60 | this.state = { |
36 | | - value: '' |
| 61 | + summary: '', |
| 62 | + description: '' |
37 | 63 | }; |
38 | 64 | } |
39 | 65 |
|
40 | | - /** Prevent enter key triggered 'submit' action during commit message input */ |
41 | | - onKeyPress(event: any): void { |
42 | | - if (event.which === 13) { |
43 | | - event.preventDefault(); |
44 | | - this.setState({ value: this.state.value + '\n' }); |
45 | | - } |
46 | | - } |
47 | | - |
48 | | - /** Initalize commit message input box */ |
49 | | - initializeInput = (): void => { |
50 | | - this.setState({ |
51 | | - value: '' |
52 | | - }); |
53 | | - }; |
54 | | - |
55 | | - /** Handle input inside commit message box */ |
56 | | - handleChange = (event: any): void => { |
57 | | - this.setState({ |
58 | | - value: event.target.value |
59 | | - }); |
60 | | - }; |
61 | | - |
62 | | - /** Update state of commit message input box */ |
63 | | - commitButtonStyle = (hasStagedFiles: boolean) => { |
64 | | - if (hasStagedFiles) { |
65 | | - if (this.state.value.length === 0) { |
66 | | - return classes(stagedCommitButtonStyle, stagedCommitButtonReadyStyle); |
67 | | - } else { |
68 | | - return stagedCommitButtonStyle; |
69 | | - } |
70 | | - } else { |
71 | | - return classes(stagedCommitButtonStyle, stagedCommitButtonDisabledStyle); |
72 | | - } |
73 | | - }; |
74 | | - |
| 66 | + /** |
| 67 | + * Renders the component. |
| 68 | + * |
| 69 | + * @returns fragment |
| 70 | + */ |
75 | 71 | render() { |
| 72 | + const disabled = !(this.props.hasFiles && this.state.summary); |
76 | 73 | return ( |
77 | | - <form |
78 | | - className={stagedCommitStyle} |
79 | | - onKeyPress={event => this.onKeyPress(event)} |
80 | | - > |
| 74 | + <form className={commitFormClass}> |
| 75 | + <input |
| 76 | + className={commitSummaryClass} |
| 77 | + type="text" |
| 78 | + placeholder="Summary (required)" |
| 79 | + title="Enter a commit message summary (a single line, preferably less than 50 characters)" |
| 80 | + value={this.state.summary} |
| 81 | + onChange={this._onSummaryChange} |
| 82 | + onKeyPress={this._onSummaryKeyPress} |
| 83 | + /> |
81 | 84 | <TextareaAutosize |
82 | | - className={classes(textInputStyle, stagedCommitMessageStyle)} |
83 | | - disabled={!this.props.hasFiles} |
84 | | - minRows={2} |
85 | | - onChange={this.handleChange} |
86 | | - placeholder={this._placeholder()} |
87 | | - value={this.state.value} |
| 85 | + className={commitDescriptionClass} |
| 86 | + minRows={5} |
| 87 | + placeholder="Description" |
| 88 | + title="Enter a commit message description" |
| 89 | + value={this.state.description} |
| 90 | + onChange={this._onDescriptionChange} |
88 | 91 | /> |
89 | 92 | <input |
90 | | - className={this.commitButtonStyle(this.props.hasFiles)} |
| 93 | + className={classes( |
| 94 | + commitButtonClass, |
| 95 | + disabled ? commitButtonDisabledClass : null |
| 96 | + )} |
91 | 97 | type="button" |
92 | 98 | title="Commit" |
93 | | - disabled={!(this.props.hasFiles && this.state.value)} |
94 | | - onClick={() => { |
95 | | - this.props.commitFunc(this.state.value); |
96 | | - this.initializeInput(); |
97 | | - }} |
| 99 | + value="Commit" |
| 100 | + disabled={disabled} |
| 101 | + onClick={this._onCommitClick} |
98 | 102 | /> |
99 | 103 | </form> |
100 | 104 | ); |
101 | 105 | } |
102 | 106 |
|
103 | | - protected _placeholder = (): string => { |
104 | | - if (this.props.settings.composite['simpleStaging']) { |
105 | | - return this.props.hasFiles |
106 | | - ? 'Input message to commit selected changes' |
107 | | - : 'Select changes to enable commit'; |
108 | | - } else { |
109 | | - return this.props.hasFiles |
110 | | - ? 'Input message to commit staged changes' |
111 | | - : 'Stage your changes before commit'; |
| 107 | + /** |
| 108 | + * Callback invoked upon clicking a commit message submit button. |
| 109 | + * |
| 110 | + * @param event - event object |
| 111 | + */ |
| 112 | + private _onCommitClick = () => { |
| 113 | + const msg = this.state.summary + '\n' + this.state.description + '\n'; |
| 114 | + this.props.onCommit(msg); |
| 115 | + |
| 116 | + // NOTE: we assume here that committing changes always works and we can safely clear component state |
| 117 | + this._reset(); |
| 118 | + }; |
| 119 | + |
| 120 | + /** |
| 121 | + * Callback invoked upon updating a commit message description. |
| 122 | + * |
| 123 | + * @param event - event object |
| 124 | + */ |
| 125 | + private _onDescriptionChange = (event: any): void => { |
| 126 | + this.setState({ |
| 127 | + description: event.target.value |
| 128 | + }); |
| 129 | + }; |
| 130 | + |
| 131 | + /** |
| 132 | + * Callback invoked upon updating a commit message summary. |
| 133 | + * |
| 134 | + * @param event - event object |
| 135 | + */ |
| 136 | + private _onSummaryChange = (event: any): void => { |
| 137 | + this.setState({ |
| 138 | + summary: event.target.value |
| 139 | + }); |
| 140 | + }; |
| 141 | + |
| 142 | + /** |
| 143 | + * Callback invoked upon a `'keypress'` event when entering a commit message summary. |
| 144 | + * |
| 145 | + * ## Notes |
| 146 | + * |
| 147 | + * - Prevents triggering a `'submit'` action when hitting the `ENTER` key while entering a commit message summary. |
| 148 | + * |
| 149 | + * @param event - event object |
| 150 | + */ |
| 151 | + private _onSummaryKeyPress(event: any): void { |
| 152 | + if (event.which === 13) { |
| 153 | + event.preventDefault(); |
112 | 154 | } |
| 155 | + } |
| 156 | + |
| 157 | + /** |
| 158 | + * Resets component state (e.g., in order to re-initialize the commit message input box). |
| 159 | + */ |
| 160 | + private _reset = (): void => { |
| 161 | + this.setState({ |
| 162 | + summary: '', |
| 163 | + description: '' |
| 164 | + }); |
113 | 165 | }; |
114 | 166 | } |
0 commit comments