Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/components/Comment.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,20 @@ export class Comment extends Component {
super(props);
const hasNote = !!(props.comment && props.comment.length > 0);
this.state = {
showCommentSection: hasNote,
showCommentSection: props.forceOpen || hasNote,
hasNote,
};
}

componentDidUpdate(prevProps) {
if (!prevProps.forceOpen && this.props.forceOpen) {
this.setState({ showCommentSection: true });
}
if (prevProps.comment && !this.props.comment) {
this.setState({ hasNote: false });
}
}

handleChange(e) {
const value = e.target.value.trim() !== '' ? e.target.value.trim() : undefined;
this.setState({
Expand Down Expand Up @@ -79,6 +88,7 @@ Comment.propTypes = {
conceptHandler: PropTypes.string,
conceptUuid: PropTypes.string,
datatype: PropTypes.string,
forceOpen: PropTypes.bool,
onCommentChange: PropTypes.func.isRequired,
value: PropTypes.string,
};
Expand Down
9 changes: 4 additions & 5 deletions src/components/ObsControl.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ export class ObsControl extends addMoreDecorator(Component) {
conceptClass,
conceptHandler,
intl,
componentStore: this.props.componentStore || ComponentStore,
});
}

Expand Down Expand Up @@ -171,7 +170,8 @@ export class ObsControl extends addMoreDecorator(Component) {
return (
<Comment
comment={comment} conceptHandler={concept.conceptHandler}
datatype={concept.datatype} onCommentChange={this.onCommentChange}
datatype={concept.datatype} forceOpen={!!properties.notesOpen}
onCommentChange={this.onCommentChange}
value={value}
/>
);
Expand All @@ -187,7 +187,7 @@ export class ObsControl extends addMoreDecorator(Component) {
const { metadata: { properties }, value } = this.props;
const isAbnormal = find(properties, (val, key) => (key === 'abnormal' && val));
const isAbnormalObs = (value.interpretation === 'ABNORMAL');
const abnormalClassName = classNames({ 'fas fa-check': isAbnormalObs });
const abnormalClassName = classNames({ 'fa fa-ok': isAbnormalObs });
if (isAbnormal) {
return (
<button className="abnormal-button" disabled={!value.value} onClick={this.setAbnormal} >
Expand Down Expand Up @@ -223,8 +223,7 @@ export class ObsControl extends addMoreDecorator(Component) {

render() {
const { concept } = this.props.metadata;
const store = this.props.componentStore || ComponentStore;
const registeredComponent = store.getRegisteredComponent(concept.datatype);
const registeredComponent = ComponentStore.getRegisteredComponent(concept.datatype);
const complexClass = Util.isComplexMediaConcept(concept) ? 'complex-component' : '';
const addMoreComplexClass = complexClass && this.isCreateByAddMore() ?
'add-more-complex-component' : '';
Expand Down
6 changes: 5 additions & 1 deletion src/components/bahmni-design-system/Comment.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class Comment extends Component {
super(props);
const hasNote = !!(props.comment && props.comment.length > 0);
this.state = {
showCommentSection: hasNote,
showCommentSection: props.forceOpen || hasNote,
hasNote,
comment: props.comment || '',
};
Expand All @@ -26,6 +26,9 @@ export class Comment extends Component {
...(hasNote && !this.state.showCommentSection ? { showCommentSection: true } : {}),
});
}
if (!prevProps.forceOpen && this.props.forceOpen) {
this.setState({ showCommentSection: true });
}
}

handleChange(e) {
Expand Down Expand Up @@ -95,6 +98,7 @@ Comment.propTypes = {
conceptHandler: PropTypes.string,
conceptUuid: PropTypes.string,
datatype: PropTypes.string,
forceOpen: PropTypes.bool,
onCommentChange: PropTypes.func.isRequired,
value: PropTypes.string,
};
3 changes: 2 additions & 1 deletion src/components/bahmni-design-system/ObsControl.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ export class ObsControl extends addMoreDecorator(Component) {
return (
<Comment
comment={comment} conceptHandler={concept.conceptHandler}
datatype={concept.datatype} onCommentChange={this.onCommentChange}
datatype={concept.datatype} forceOpen={!!properties.notesOpen}
onCommentChange={this.onCommentChange}
value={value}
/>
);
Expand Down
57 changes: 57 additions & 0 deletions src/helpers/ControlRecordWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,61 @@ export default class ControlRecordWrapper {
}
return updatedRecord;
}

isNotesEnabled() {
if (this.currentRecord && this.currentRecord.control && this.currentRecord.control.properties) {
return this.currentRecord.control.properties.notes;
}
return false;
}

getNotes() {
if (this.currentRecord.control.type !== 'obsControl') {
return null;
}
if (this.currentRecord && this.currentRecord.value && this.currentRecord.value.comment) {
return this.currentRecord.value.comment;
}
return null;
}

showNotes(show = true) {
const brotherTrees = ControlRecordTreeMgr.getBrothers(this.rootRecord, this.currentRecord);
brotherTrees.forEach(r => {
if (r.control.type !== 'obsControl' || r.control.properties === undefined) {
return;
}
if (Object.hasOwn(r.control.properties, 'notes')) {
var control = Object.assign({}, r.control);
control.properties.notes = show;
const updatedRecord = r.set('control', control);
this.update(updatedRecord);
}
});
}

openNotes() {
const brotherTrees = ControlRecordTreeMgr.getBrothers(this.rootRecord, this.currentRecord);
brotherTrees.forEach(r => {
if (r.control.type !== 'obsControl' || r.control.properties === undefined) {
return;
}
var control = Object.assign({}, r.control);
control.properties.notesOpen = true;
const updatedRecord = r.set('control', control);
this.update(updatedRecord);
});
}

clearNotes() {
const brotherTrees = ControlRecordTreeMgr.getBrothers(this.rootRecord, this.currentRecord);
brotherTrees.forEach(r => {
if (r.control.type !== 'obsControl') {
return;
}
const updatedRecord = r.set('value', Object.assign({}, r.value, { comment: undefined }));
this.update(updatedRecord);
});
}

}
4 changes: 2 additions & 2 deletions src/helpers/controlsParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ export function getControls(controls, records, props) {
...props,
enabled: record.enabled && props.enabled,
hidden: record.hidden,
key: control.id,
metadata: control,
key: record.control.id,
metadata: record.control,
value: record.value,
formFieldPath: record.formFieldPath,
showAddMore: record.showAddMore,
Expand Down
45 changes: 43 additions & 2 deletions test/components/Comment.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,9 @@ describe('Comment', () => {
it('should render comment section with default value', () => {
render(<Comment comment="Some Comment" onCommentChange={mockOnCommentChange} />);

// Textarea is auto-expanded when an existing comment is provided — no click needed.
expect(screen.getByRole('textbox')).toHaveValue('Some Comment');
const textarea = screen.getByRole('textbox');

expect(textarea).toHaveValue('Some Comment');
});

it('should not render comment button when the control is of complex media type', () => {
Expand Down Expand Up @@ -131,4 +132,44 @@ describe('Comment', () => {

expect(screen.queryByRole('textbox')).not.toBeInTheDocument();
});

describe('forceOpen', () => {
it('should render comment section open when forceOpen is true on initial render', () => {
render(<Comment forceOpen onCommentChange={mockOnCommentChange} />);

expect(screen.getByRole('textbox')).toBeInTheDocument();
});

it('should open comment section when forceOpen prop transitions from false to true', () => {
const { rerender } = render(<Comment forceOpen={false} onCommentChange={mockOnCommentChange} />);
expect(screen.queryByRole('textbox')).not.toBeInTheDocument();

rerender(<Comment forceOpen onCommentChange={mockOnCommentChange} />);

expect(screen.getByRole('textbox')).toBeInTheDocument();
});

it('should not close comment section when forceOpen transitions from true to false', () => {
const { rerender } = render(<Comment forceOpen onCommentChange={mockOnCommentChange} />);
expect(screen.getByRole('textbox')).toBeInTheDocument();

rerender(<Comment forceOpen={false} onCommentChange={mockOnCommentChange} />);

expect(screen.getByRole('textbox')).toBeInTheDocument();
});
});

describe('clearNotes', () => {
it('should remove has-notes class from button when comment prop is cleared', () => {
const { rerender } = render(
<Comment comment="existing note" onCommentChange={mockOnCommentChange} />
);
fireEvent.click(screen.getByRole('button'));
expect(screen.getByRole('button')).toHaveClass('has-notes');

rerender(<Comment comment={undefined} onCommentChange={mockOnCommentChange} />);

expect(screen.getByRole('button')).not.toHaveClass('has-notes');
});
});
});
15 changes: 15 additions & 0 deletions test/components/bahmni-design-system/Comment.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,21 @@ describe('Carbon Comment', () => {
expect(link).toHaveAttribute('aria-expanded', 'false');
});

it('should auto-open textarea when forceOpen is true on initial render', () => {
render(<Comment forceOpen onCommentChange={mockOnCommentChange} />);
expect(screen.getByRole('textbox')).toBeInTheDocument();
});

it('should auto-open textarea when forceOpen transitions from false to true', () => {
const { rerender } = render(
<Comment forceOpen={false} onCommentChange={mockOnCommentChange} />
);
expect(screen.queryByRole('textbox')).not.toBeInTheDocument();

rerender(<Comment forceOpen onCommentChange={mockOnCommentChange} />);
expect(screen.getByRole('textbox')).toBeInTheDocument();
});

it('should auto-expand comment section when comment prop arrives after initial render', () => {
const { rerender } = render(<Comment onCommentChange={mockOnCommentChange} />);

Expand Down
28 changes: 28 additions & 0 deletions test/components/bahmni-design-system/ObsControl.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,32 @@ describe('Carbon ObsControl', () => {
expect(mockOnValueChanged).not.toHaveBeenCalled();
});
});

describe('Notes (showComment)', () => {
it('should not render the notes section when notes property is not set', () => {
renderWithIntl(<ObsControlWithIntl {...defaultProps} />);
expect(screen.queryByRole('button', { name: /add note/i })).not.toBeInTheDocument();
});

it('should render the add note link when notes property is enabled', () => {
const notesMetadata = { ...defaultMetadata, properties: { notes: true } };
renderWithIntl(
<ObsControlWithIntl {...defaultProps} metadata={notesMetadata} />
);
expect(screen.getByRole('button', { name: /add note/i })).toBeInTheDocument();
expect(screen.queryByPlaceholderText('Notes')).not.toBeInTheDocument();
});

it('should auto-open textarea when notesOpen is true', () => {
const notesOpenMetadata = {
...defaultMetadata,
properties: { notes: true, notesOpen: true },
};
renderWithIntl(
<ObsControlWithIntl {...defaultProps} metadata={notesOpenMetadata} />
);
expect(screen.getByRole('button', { name: /add note/i })).toBeInTheDocument();
expect(screen.getByPlaceholderText('Notes')).toBeInTheDocument();
});
});
});
Loading
Loading