Skip to content

Commit

Permalink
refactor: convert DGTextInput from v2 pre-ES6 JavaScript to ES6 class…
Browse files Browse the repository at this point in the history
… in TypeScript (#1327)
  • Loading branch information
kswenson authored Jul 11, 2024
1 parent 8aa5911 commit 56dca62
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 121 deletions.
4 changes: 2 additions & 2 deletions v3/src/components/case-card/attribute-value-cell.v2.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
114 changes: 0 additions & 114 deletions v3/src/components/case-card/text-input.v2.js

This file was deleted.

10 changes: 5 additions & 5 deletions v3/src/components/case-card/text-input.v2.test.tsx
Original file line number Diff line number Diff line change
@@ -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 () => {
Expand All @@ -14,7 +12,8 @@ describe("Case card TextInput", () => {

// renders the value by default
const { container, rerender } = render(
<TextInput value="foo" isEditable={true} onToggleEditing={mockOnToggleEditing}/>
<DGTextInput value="foo" isEditable={true} onEditModeCallback={mockOnEditModeCallback}
onEscapeEditing={mockOnEscapeEditing} onToggleEditing={mockOnToggleEditing}/>
)
const valueSpan = screen.getByText("foo")
expect(valueSpan).toBeInTheDocument()
Expand All @@ -25,12 +24,13 @@ describe("Case card TextInput", () => {

// createInEditMode controls editing
rerender(
<TextInput value="foo" isEditable={true}
<DGTextInput value="foo" isEditable={true}
createInEditMode={true} onEditModeCallback={mockOnEditModeCallback}
onEscapeEditing={mockOnEscapeEditing} onToggleEditing={mockOnToggleEditing}/>
)
const inputElt = screen.getByDisplayValue("foo")
expect(inputElt).toBeInTheDocument()
expect(inputElt).toHaveFocus()
expect(mockOnEditModeCallback).toHaveBeenCalledTimes(1)

// can type into text field
Expand Down
113 changes: 113 additions & 0 deletions v3/src/components/case-card/text-input.v2.tsx
Original file line number Diff line number Diff line change
@@ -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<IProps, IState> {

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<HTMLInputElement>) => {
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
? <input type='text' className='react-data-card-value-input'
// ref is called on creation of the input element
ref={input => {
this.inputElement = input
input?.focus()
}}
value={this.state.value}
onChange={this.handleChange}
onKeyDown={this.handleKeyDown}
onFocus={iEvent => iEvent.target.select()}
/>
: <span className={clsx(this.props.className, { 'react-data-card-value': this.props.isEditable })}
title={`${tValue}`}
onClick={() => {
if (!this.state.editing && this.props.isEditable) {
this.props.onToggleEditing(this)
}
}}
>
{tValue + tUnits}
</span>
}

}

0 comments on commit 56dca62

Please sign in to comment.