Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ui.text_area #652

Merged
merged 10 commits into from
Jul 30, 2024
Merged

feat: ui.text_area #652

merged 10 commits into from
Jul 30, 2024

Conversation

AkshatJawne
Copy link
Contributor

@AkshatJawne AkshatJawne commented Jul 22, 2024

Closes #649

Additional Info:

Some of the snippets I had tested with:

from deephaven import ui

def test_on_change(new_value):
    print(f"Text changed to {new_value}")

t1 = ui.text_area()
t2 = ui.text_area(on_change=test_on_change)
t3 = ui.text_area(label="Test Label", label_position='side', flex_grow=1.0, flex_shrink=0.75)
t4 = ui.text_area(label="Test Label 2", is_required=True)
t5 = ui.text_area(is_disabled=True, is_hidden=False)
t6 = ui.text_area(is_hidden=True)
t7 = ui.text_area(auto_focus=True)

@AkshatJawne AkshatJawne requested a review from mofojed July 22, 2024 20:53
@AkshatJawne AkshatJawne self-assigned this Jul 22, 2024
Copy link
Member

@mofojed mofojed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Include some examples that you tested with. Looking good

Comment on lines 27 to 65
const [value, setValue] = useState(propValue ?? defaultValue);
const [pending, setPending] = useState(false);
const prevPropValue = usePrevious(propValue);

// Update local value to new propValue if the server sent a new propValue and no user changes have been queued
if (
propValue !== prevPropValue &&
propValue !== value &&
propValue !== undefined &&
!pending
) {
setValue(propValue);
}

const propDebouncedOnChange = useCallback(
async (newValue: string) => {
try {
await propOnChange(newValue);
} catch (e) {
log.warn('Error returned from onChange', e);
}
setPending(false);
},
[propOnChange]
);

const debouncedOnChange = useDebouncedCallback(
propDebouncedOnChange,
VALUE_CHANGE_DEBOUNCE
);

const onChange = useCallback(
(newValue: string) => {
setPending(true);
debouncedOnChange(newValue);
setValue(newValue);
},
[debouncedOnChange]
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be able to extract this into a hook and share it with TextField. Something like const [value, onChange] = useDebouncedValue<T = string>(propValue, defaultValue, propOnChange)

VALUE_CHANGE_DEBOUNCE
);

const onChange = useCallback(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note about onChange - we should only have it set to a defined value if the passed in propOnChange is defined. In some cases, simply passing in a function will change the behaviour of the component, so we don't want to always pass in a function if the propOnChange function was not provided.

So in this case it'd be something like:

const onChangeCallback = useCallback( ... );
const onChange = propOnChange ? onChangeCallback : undefined;

See useFormEventCallback.ts as another example (and it's usage in Form.tsx).



def text_area(
icon: Element | None = None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For convenience, we can probably allow an icon string here as well, and automatically convert it to a ui.icon element if it's provided.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to do this though, seems inconsistent since we don't do it tab.py , text_field.py, list_action_group.py with summary_icon?

@AkshatJawne AkshatJawne requested a review from mofojed July 25, 2024 05:53

const VALUE_CHANGE_DEBOUNCE = 250;

function useDebouncedOnChange<T = string>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hook should probably be implemented in @deephaven/react-hooks. Doesn't seem to have anything plugins specific unless I'm missing someting.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AkshatJawne Does this add any functionality that useDebouncedCallback doesn't already provide?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nvm, I see what it is doing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can look into implementing on the DHC side, thought it would be more appropriate here, since Bender had referenced the useFormEventCallback.ts, and that lives in the plugins repo.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is useFormEventCallback referenced?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's just an example of a hook that properly handles the undefined callback scenario and not necessarily tying it to plugins repo.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't be opposed to it, if @mofojed has no issues with it, I can do it -- just holds up the process a little more, have to create a separate web-client-ui PR, wait for that to merge, and then wait for the version bump and then this will be able to be merged, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to move it to @deephaven/react-hooks - we can keep it in mind but there's no pressing need to, we don't need to wait for a bump/publish cycle to use it here, just add it here.


function useDebouncedOnChange<T = string>(
propValue: T | undefined,
defaultValue: T,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the defaultValue prop makes sense inside of this hook. I would just support value and onChange inputs and coalesce the value ?? defaultValue in the calling component.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good shout, yeah I think this makes sense, will make change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a chance you can use useDebouncedValue and don't need a new hook at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gonna try rn

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nvm, I don't think that allows the debouncing in the change handler passed into the component

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although, just on first sight, I will need onChange for the textField, which the useDebouncedValue doesn't give access to

Copy link
Contributor

@bmingles bmingles Jul 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, disregard the useDebouncedValue idea :)

@AkshatJawne AkshatJawne requested a review from bmingles July 29, 2024 13:06
@AkshatJawne AkshatJawne requested a review from bmingles July 29, 2024 15:14
plugins/ui/src/deephaven/ui/types/types.py Outdated Show resolved Hide resolved
plugins/ui/src/js/src/elements/TextArea.tsx Outdated Show resolved Hide resolved
Comment on lines 30 to 37
const icon =
typeof propIcon === 'string' ? (
<Icon>
<FontAwesomeIcon icon={getIcon(`deephaven.ui.icons.${propIcon}`)} />
</Icon>
) : (
propIcon
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer this on the Python side. Just change icon=icon on text_area.py#187 to icon=ui.icon(icon) if type(icon) == 'str' else icon and it should be good.
It's better practice to do it Python side when possible as we may have default props set in the Python code.

Copy link
Contributor Author

@AkshatJawne AkshatJawne Jul 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Been trying to work with this, causing an error on rendering, trying to debug:
Screenshot 2024-07-29 at 10 21 30 AM

@AkshatJawne AkshatJawne requested a review from mofojed July 30, 2024 00:51
@AkshatJawne AkshatJawne mentioned this pull request Jul 30, 2024
@AkshatJawne AkshatJawne merged commit 5fb24bc into deephaven:main Jul 30, 2024
15 checks passed
AkshatJawne added a commit that referenced this pull request Aug 7, 2024
Closes #678

Assuming the changes proposed in #652
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ui.text_area
3 participants