Skip to content

Commit

Permalink
Update to react-md branch using TextArea AutoComplete (#390)
Browse files Browse the repository at this point in the history
Signed-off-by: Carl Gieringer <78054+carlgieringer@users.noreply.github.com>
  • Loading branch information
carlgieringer authored Apr 21, 2023
1 parent df51310 commit f52d461
Show file tree
Hide file tree
Showing 11 changed files with 446 additions and 225 deletions.
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@
},
"packageManager": "yarn@3.3.1",
"resolutions": {
"react-native@^0.66": "patch:react-native@npm%3A0.66.5#./.yarn/patches/react-native-npm-0.66.5-22e5dd8ec5.patch",
"@react-md/autocomplete@^2": "patch:@react-md/autocomplete@npm%3A2.9.1#./.yarn/patches/@react-md-autocomplete-npm-2.9.1-0914f23b5c.patch"
"react-native@^0.66": "patch:react-native@npm%3A0.66.5#./.yarn/patches/react-native-npm-0.66.5-22e5dd8ec5.patch"
}
}
26 changes: 22 additions & 4 deletions premiser-ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,26 @@ hot loaders interaction with react-dom, so I just switched to `yarn add 'react-d

## react-md

This workspace currently depends on a patch of @react-md/autocomplete@2.9.1 that adds
`event.preventDefault()` when the user presses enter to autocomplete. This change prevents
submitting a form when the user is autocompleting.
This workspace currently depends on a branch in a fork of react-md. This fork:

PR for that change is here: https://github.com/mlaursen/react-md/pull/1439
- adds `event.preventDefault()` when the user presses enter to autocomplete. This change prevents
submitting a form when the user is autocompleting.
[PR](https://github.com/mlaursen/react-md/pull/1439)
- uses TextArea in AutoComplete instead of TextInput. (See autocomplete requires in
[this comment](https://github.com/Howdju/howdju/issues/304#issuecomment-1511515470).)
- upgrades the repo to Yarn berry so that I can add it (command below)

The steps to add this dependency are to run this command:

```sh
yarn add '@react-md/autocomplete@Howdju/react-md#workspace=@react-md/autocomplete&commit=c5bb05609126221985da30395a71cfdebd9d07d9'
```

And then manually to edit the `yarn.lock` entry for @react-md/autocomplete to point @react-md/form
to the same version:

```diff
dependencies:
- "@react-md/form": ^2.9.1
+ "@react-md/form": https://github.com/Howdju/react-md.git#workspace=%40react-md%2Fform&commit=c5bb05609126221985da30395a71cfdebd9d07d9
```
3 changes: 1 addition & 2 deletions premiser-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,7 @@
},
"dependencies": {
"@grrr/cookie-consent": "^1.0.4",
"@react-md/autocomplete": "^2",
"@react-md/form": "^2",
"@react-md/autocomplete": "Howdju/react-md#workspace=@react-md/autocomplete&commit=c5bb05609126221985da30395a71cfdebd9d07d9",
"@react-md/icon": "^2",
"@react-md/layout": "^2",
"@react-md/menu": "^2",
Expand Down
13 changes: 13 additions & 0 deletions premiser-ui/src/ApiAutoComplete.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
@import "~@react-md/form/dist/mixins";

.rmd-floating-label {
// TODO(17) figure out the source of this (.md-text--secondary?)
color: rgba(0, 0, 0, 0.54);
}

.api-autocomplete {
.rmd-textarea {
// The default text-height is too large.
@include rmd-form-theme-update-var(text-height, 2rem);
}
.rmd-floating-label {
// The label isn't far enough down.
@include rmd-form-theme-update-var(floating-top, 1.7rem);
}
}
38 changes: 29 additions & 9 deletions premiser-ui/src/ApiAutoComplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import React, {
} from "react";
import { PayloadAction } from "@reduxjs/toolkit";
import { AutoComplete, AutoCompleteResult } from "@react-md/autocomplete";
import { FormMessage } from "@react-md/form";
import { useDebouncedCallback } from "use-debounce";
import { denormalize, Schema } from "normalizr";

Expand Down Expand Up @@ -57,6 +56,17 @@ export interface Props<T>
/** Controls to display to the right of the input. */
rightControls?: ReactNode;
onAutoComplete?: (suggestion: T) => void;
/**
* The number of rows to display by default. The textarea will automatically
* update and animate its height when the users types if the `resize` prop is
* set to `"auto"`.
*/
rows?: number;
/**
* The maximum number of rows that are allowed. When this is set to `-1`, it
* will infinitely expand based on the text content.
*/
maxRows?: number;
}

export default function ApiAutoComplete({
Expand All @@ -75,14 +85,17 @@ export default function ApiAutoComplete({
errorText,
rightControls,
onAutoComplete,
rows = 1,
maxRows = -1,
maxLength,
...rest
}: Props<any>) {
const dispatch = useAppDispatch();
const debouncedFetchSuggestions = useDebouncedCallback((value: string) => {
dispatch(fetchSuggestions(value, suggestionsKey));
}, autocompleteDebounceMs);

function onChange(event: ChangeEvent<HTMLInputElement>) {
function onChange(event: ChangeEvent<HTMLTextAreaElement>) {
const name = event.target.name;
const value = singleLine
? toSingleLine(event.target.value)
Expand All @@ -106,7 +119,7 @@ export default function ApiAutoComplete({
function clearSuggestions() {
dispatch(autocompletes.clearSuggestions(suggestionsKey));
}
function _onBlur(event: FocusEvent<HTMLInputElement>) {
function _onBlur(event: FocusEvent<HTMLTextAreaElement>) {
if (onBlur) {
onBlur(event.target.name);
}
Expand All @@ -130,32 +143,39 @@ export default function ApiAutoComplete({
}
}

const messageProps =
error && errorText
? {
error,
children: <span>{errorText}</span>,
}
: undefined;

return (
<>
<div style={{ display: "flex", alignItems: "center" }}>
<AutoComplete
{...rest}
id={id}
name={name}
className="api-autocomplete"
data={suggestionsData}
labelKey={labelKey}
valueKey={labelKey}
onBlur={_onBlur}
onChange={onChange}
onAutoComplete={_onAutoComplete}
error={error}
disableShowOnFocus
style={{ flexGrow: 1 }}
theme="underline"
filter="none"
rows={rows}
maxRows={maxRows}
maxLength={maxLength}
messageProps={messageProps}
/>
{rightControls}
</div>
{error && errorText && (
<FormMessage id={`${id}-error`} error>
{errorText}
</FormMessage>
)}
</>
);
}
69 changes: 27 additions & 42 deletions premiser-ui/src/TagsControl.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FormEvent, useState } from "react";
import React, { useState } from "react";
import find from "lodash/find";
import get from "lodash/get";
import includes from "lodash/includes";
Expand Down Expand Up @@ -69,7 +69,7 @@ export default function TagsControl(props: Props) {
tags,
votes,
recommendedTags,
commitChipKeys = [Keys.COMMA],
commitChipKeys = [Keys.COMMA, Keys.ENTER],
id,
suggestionsKey,
votePolarity = {
Expand All @@ -89,24 +89,17 @@ export default function TagsControl(props: Props) {
const [tagName, setTagName] = useState("");
const [isInputCollapsed, setIsInputCollapsed] = useState(true);

function submitTagName(event: FormEvent<HTMLFormElement>) {
// Stop it from submitting an enclosing form
event.preventDefault();
// Stop it from propagating to an enclosing form
event.stopPropagation();

addTag(tagName);
setTagName("");
if (inputCollapsable) {
setIsInputCollapsed(true);
}
}

const onTagNameKeyDown: OnKeyDownCallback = (event) => {
if (event.isDefaultPrevented()) {
return;
}
if (includes(commitChipKeys, event.key) && tagName) {
event.preventDefault();
addTag(tagName);
setTagName("");
if (inputCollapsable) {
setIsInputCollapsed(true);
}
}
};

Expand Down Expand Up @@ -185,33 +178,25 @@ export default function TagsControl(props: Props) {
const extraChildren = [];
if (!inputCollapsable || !isInputCollapsed) {
extraChildren.push(
// This form makes it easier to receive a submit only when the autocomplete listbox is closed.
// But it also makes it possible for us to nest forms, which is tecnically invalid HTML.
// Other solutions include updating AutoComplete to call onKeyDown after it has
// preventedDefault so that our onKeyDown could inspect that and do nothing. Or to define a
// custom onKeyDown that passes information about the current AutoComplete state, like whether
// the listbox is visible.
<form onSubmit={submitTagName}>
<TagNameAutocomplete
id={tagNameAutocompleteId}
key={tagNameAutocompleteId}
name="tagName"
value={tagName}
className="tag-name-autocomplete"
suggestionsKey={suggestionsKey}
onAutoComplete={onTagNameAutocomplete}
onPropertyChange={onTagNamePropertyChange}
onKeyDown={onTagNameKeyDown}
rightControls={
inputCollapsable ? (
<Button icon onClick={() => closeInput()}>
done
</Button>
) : undefined
}
autocompleteDebounceMs={autocompleteDebounceMs}
/>
</form>
<TagNameAutocomplete
id={tagNameAutocompleteId}
key={tagNameAutocompleteId}
name="tagName"
value={tagName}
className="tag-name-autocomplete"
suggestionsKey={suggestionsKey}
onAutoComplete={onTagNameAutocomplete}
onPropertyChange={onTagNamePropertyChange}
onKeyDown={onTagNameKeyDown}
rightControls={
inputCollapsable ? (
<Button icon onClick={() => closeInput()}>
done
</Button>
) : undefined
}
autocompleteDebounceMs={autocompleteDebounceMs}
/>
);
}
if (inputCollapsable && isInputCollapsed) {
Expand Down
Loading

0 comments on commit f52d461

Please sign in to comment.