diff --git a/v3/src/components/case-card/attribute-value-cell.v2.js b/v3/src/components/case-card/attribute-value-cell.v2.js index c99af2e84a..4553733aa6 100644 --- a/v3/src/components/case-card/attribute-value-cell.v2.js +++ b/v3/src/components/case-card/attribute-value-cell.v2.js @@ -5,7 +5,7 @@ import { createReactFC, DG } from "../../v2/dg-compat.v2" import { SC } from "../../v2/sc-compat" import { tinycolor } from "../../utilities/color-utils" import "./attribute-summary.v2" -import "./text-input.v2" +import { DGTextInput } from "./text-input.v2" DG.React.ready(function () { var img = ReactDOMFactories.img, @@ -184,7 +184,7 @@ DG.React.ready(function () { attr: tAttr, unit: tUnit }) - : DG.React.Components.TextInput({ + : React.createElement(DGTextInput, { attr: tAttr, 'case': props.editableCase, value: tValue, diff --git a/v3/src/components/case-card/text-input.v2.js b/v3/src/components/case-card/text-input.v2.js deleted file mode 100644 index 76ef7b36b6..0000000000 --- a/v3/src/components/case-card/text-input.v2.js +++ /dev/null @@ -1,114 +0,0 @@ -import ReactDOMFactories from "react-dom-factories" -import { DG } from "../../v2/dg-compat.v2" -import { SC } from "../../v2/sc-compat" - -DG.React.ready(function () { - var - span = ReactDOMFactories.span, - input = ReactDOMFactories.input - - DG.React.Components.TextInput = DG.React.createComponent( - (function () { - - /** - * props are - * value: {string | number} - * unit: {string} - * isEditable: {boolean} - * createInEditMode: {boolean} - * onEscapeEditing: {function} - * onToggleEditing: {function} - */ - - return { - inputElement: null, - - getInitialState () { - return { - editing: false, - value: this.props.value, - unit: this.props.unit - } - }, - - componentDidMount () { - window.addEventListener("touchstart", this._onWindowClick, true) - window.addEventListener("mousedown", this._onWindowClick, true) - }, - componentWillUnmount () { - window.removeEventListener("touchstart", this._onWindowClick, true) - window.removeEventListener("mousedown", this._onWindowClick, true) - }, - - UNSAFE_componentWillReceiveProps (iNewProps) { - if (iNewProps.value !== this.state.value) - { this.setState({value: iNewProps.value}) } - if (iNewProps.unit !== this.state.unit) - { this.setState({unit: iNewProps.unit}) } - if (iNewProps.createInEditMode && iNewProps.onEditModeCallback) - { iNewProps.onEditModeCallback(this) } - }, - - componentDidUpdate(iPrevProps) { - if (this.props.createInEditMode !== iPrevProps.createInEditMode) { - this.setState({ editing: this.props.createInEditMode }) - } - }, - - _onWindowClick (event) { - if (this.inputElement && this.state.editing && - (event.target !== this.inputElement) && !this.inputElement.contains(event.target)) { - this.props.onToggleEditing(this) - } - }, - - handleChange (iEvent) { - this.setState({value: iEvent.target.value}) - }, - - render () { - var - this_ = this, - tUnits = SC.empty(this.state.value) ? '' : ` ${ this.state.unit || ''}`, - tValueClassName = this.props.isEditable ? 'react-data-card-value ' : '', - tValue = SC.empty(this.state.value) ? '____' : this.state.value, - kCompletionCodes = [13, 9], - tResult = this.state.editing - ? input({ - className: 'react-data-card-value-input dg-wants-mouse dg-wants-touch', - type: 'text', - // ref is called on creation of the input element - ref(input) { - this_.inputElement = input - input?.focus() - }, - value: this.state.value, - onChange: this.handleChange, - onKeyDown: function (iEvent) { - if (kCompletionCodes.indexOf(iEvent.keyCode) >= 0) { - this.props.onToggleEditing(this, iEvent.shiftKey ? 'up' : 'down') -iEvent.preventDefault() - } - else if (iEvent.keyCode === 27) { - this.props.onEscapeEditing(this) - } - }.bind(this), - onFocus(iEvent) { - iEvent.target.select() - } - }) - : span({ - className: tValueClassName + (this.props.className || ''), - title: tValue, - onClick: function (iEvent) { - if (!this.state.editing && this.props.onToggleEditing && this.props.isEditable) { - this.props.onToggleEditing(this) - } - }.bind(this) - }, tValue + tUnits) - return tResult - } - } - }()), []) - -}) diff --git a/v3/src/components/case-card/text-input.v2.test.tsx b/v3/src/components/case-card/text-input.v2.test.tsx index a127e10bfa..76b3c65069 100644 --- a/v3/src/components/case-card/text-input.v2.test.tsx +++ b/v3/src/components/case-card/text-input.v2.test.tsx @@ -1,9 +1,7 @@ import { fireEvent, render, screen } from "@testing-library/react" import { userEvent } from '@testing-library/user-event' import React from "react" -import { DG } from "../../v2/dg-compat.v2" -import "./text-input.v2" -const { TextInput } = DG.React.Components as any +import { DGTextInput } from "./text-input.v2" describe("Case card TextInput", () => { it("works as expected", async () => { @@ -14,7 +12,8 @@ describe("Case card TextInput", () => { // renders the value by default const { container, rerender } = render( - + ) const valueSpan = screen.getByText("foo") expect(valueSpan).toBeInTheDocument() @@ -25,12 +24,13 @@ describe("Case card TextInput", () => { // createInEditMode controls editing rerender( - ) const inputElt = screen.getByDisplayValue("foo") expect(inputElt).toBeInTheDocument() + expect(inputElt).toHaveFocus() expect(mockOnEditModeCallback).toHaveBeenCalledTimes(1) // can type into text field diff --git a/v3/src/components/case-card/text-input.v2.tsx b/v3/src/components/case-card/text-input.v2.tsx new file mode 100644 index 0000000000..97527d2495 --- /dev/null +++ b/v3/src/components/case-card/text-input.v2.tsx @@ -0,0 +1,113 @@ +import { clsx } from "clsx" +import React, { Component } from "react" + +interface IProps { + className?: string + value: string + unit?: string + isEditable: boolean + createInEditMode?: boolean + onEditModeCallback: (input: DGTextInput) => void + onEscapeEditing: (input: DGTextInput) => void + onToggleEditing: (input: DGTextInput, moveDirection?: "up" | "down") => void +} + +interface IState { + editing: boolean + value: string + unit?: string +} + +export class DGTextInput extends Component { + + inputElement: HTMLInputElement | null = null + + constructor(props: IProps) { + super(props) + + this.state = { + editing: false, + value: props.value, + unit: props.unit + } + } + + componentDidMount () { + window.addEventListener("touchstart", this._onWindowClick, true) + window.addEventListener("mousedown", this._onWindowClick, true) + } + componentWillUnmount () { + window.removeEventListener("touchstart", this._onWindowClick, true) + window.removeEventListener("mousedown", this._onWindowClick, true) + } + + UNSAFE_componentWillReceiveProps (iNewProps: IProps) { + if (iNewProps.value !== this.state.value) + { this.setState({value: iNewProps.value}) } + if (iNewProps.unit !== this.state.unit) + { this.setState({unit: iNewProps.unit}) } + if (iNewProps.createInEditMode && iNewProps.onEditModeCallback) + { iNewProps.onEditModeCallback(this) } + } + + componentDidUpdate(iPrevProps: IProps) { + if (this.props.createInEditMode !== iPrevProps.createInEditMode) { + this.setState({ editing: !!this.props.createInEditMode }) + } + } + + isValueEmpty() { + return this.state.value == null || this.state.value === '' + } + + _onWindowClick = (event: MouseEvent | TouchEvent) => { + if (this.inputElement && this.state.editing && + (event.target !== this.inputElement) && !this.inputElement.contains(event.target as Node | null)) { + this.props.onToggleEditing(this) + } + } + + handleChange = (iEvent: React.ChangeEvent) => { + this.setState({value: iEvent.target.value}) + } + + handleKeyDown = (iEvent: React.KeyboardEvent) => { + const kCompletionCodes = [13, 9] + if (kCompletionCodes.indexOf(iEvent.keyCode) >= 0) { + this.props.onToggleEditing(this, iEvent.shiftKey ? 'up' : 'down') + iEvent.preventDefault() + } + else if (iEvent.keyCode === 27) { + this.props.onEscapeEditing(this) + } + } + + render () { + const + tUnits = this.isValueEmpty() ? '' : ` ${this.state.unit || ''}`, + tValue = this.isValueEmpty() ? '____' : this.state.value + return this.state.editing + ? { + this.inputElement = input + input?.focus() + }} + value={this.state.value} + onChange={this.handleChange} + onKeyDown={this.handleKeyDown} + onFocus={iEvent => iEvent.target.select()} + /> + : { + if (!this.state.editing && this.props.isEditable) { + this.props.onToggleEditing(this) + } + }} + > + {tValue + tUnits} + + } + +}