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!: update Autosuggest component to better support freeform and selected values #2899

Conversation

brian-smith-tcril
Copy link
Contributor

@brian-smith-tcril brian-smith-tcril commented Dec 7, 2023

Description

This updates the Autosuggest component to provide the consumer with:

  • The value entered in the textbox by the user
  • The selected value (as it is displayed in the dropdown)
  • The id of the selected option

This also allows the consumer to determine if the Autosuggest component should be in an error state when

  • No text has been entered
  • Text has been entered, but not selection has been made
  • An external validation check has failed - "customError"

The consumer will provide error messages for the appropriate error states

BREAKING CHANGE: value of Autosuggest component is now an object instead of a string
BREAKING CHANGE: Autosuggest component now uses onChange instead of onSelected
BREAKING CHANGE: Autosuggest component now takes in different error messages for value/selection required, and custom errors

Deploy Preview

https://deploy-preview-2899--paragon-openedx.netlify.app/components/form/form-autosuggest/

Merge Checklist

  • If your update includes visual changes, have they been reviewed by a designer? Send them a link to the Netlify deploy preview, if applicable.
  • Does your change adhere to the documented style conventions?
  • Do any prop types have missing descriptions in the Props API tables in the documentation site (check deploy preview)?
  • Were your changes tested using all available themes (see theme switcher in the header of the deploy preview, under the "Settings" icon)?
  • Were your changes tested in the example app?
  • Is there adequate test coverage for your changes?
  • Consider whether this change needs to reviewed/QA'ed for accessibility (a11y). If so, please add wittjeff and adamstankiewicz as reviewers on this PR.

Post-merge Checklist

  • Verify your changes were released to NPM at the expected version.
  • If you'd like, share your contribution in #show-and-tell.
  • 🎉 🙌 Celebrate! Thanks for your contribution.

Copy link

netlify bot commented Dec 7, 2023

Deploy Preview for paragon-openedx ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit e3daacf
🔍 Latest deploy log https://app.netlify.com/sites/paragon-openedx/deploys/65a6f936ce5aef0007cfe4fc
😎 Deploy Preview https://deploy-preview-2899--paragon-openedx.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link

codecov bot commented Dec 7, 2023

Codecov Report

Attention: 1 lines in your changes are missing coverage. Please review.

Comparison is base (8c09b4f) 93.07% compared to head (e3daacf) 93.18%.

Files Patch % Lines
src/Form/FormAutosuggest.jsx 99.28% 1 Missing ⚠️
Additional details and impacted files
@@                           Coverage Diff                           @@
##           release/chip-searchfield-pagination    #2899      +/-   ##
=======================================================================
+ Coverage                                93.07%   93.18%   +0.10%     
=======================================================================
  Files                                      249      249              
  Lines                                     4306     4342      +36     
  Branches                                  1029     1036       +7     
=======================================================================
+ Hits                                      4008     4046      +38     
+ Misses                                     294      292       -2     
  Partials                                     4        4              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@brian-smith-tcril brian-smith-tcril changed the title update Autosuggest component to better support freeform and selected values feat!: update Autosuggest component to better support freeform and selected values Dec 7, 2023
/** Specifies if empty values trigger an error state */
valueRequired: PropTypes.bool,
/** Informs user they must input a value. */
valueRequiredErrorMessageText: PropTypes.string,
Copy link
Contributor

Choose a reason for hiding this comment

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

I assume that there should be valueRequiredErrorMessageText provided if valueRequired equals true. Please, consider:

import { requiredWhen } from '../utils/propTypes';

...

valueRequiredErrorMessageText: requiredWhen(PropTypes.string, 'valueRequired'),

And the same for selectionRequired and selectionRequiredErrorMessageText, customError and customErrorMessageText.

Another option, if no message is provided and there should only be a red box to indicate the error, I would remove the x under the input, otherwise it will appear incomplete.image

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 call! I was debating including default error messages, but I think using requiredWhen is the cleanest solution!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@monteri I have updated these to use requiredWhen

/** Informs user they must input a value. */
valueRequiredErrorMessageText: requiredWhen(PropTypes.string, 'valueRequired'),
/** Specifies if freeform values trigger an error state */
selectionRequired: PropTypes.bool,
/** Informs user they must make a selection. */
selectionRequiredErrorMessageText: requiredWhen(PropTypes.string, 'selectionRequired'),
/** Specifies the control is in a consumer provided error state */
customError: PropTypes.bool,
/** Informs user of other errors. */
customErrorMessageText: requiredWhen(PropTypes.string, 'customError'),

@brian-smith-tcril brian-smith-tcril force-pushed the autosuggest-rework branch 2 times, most recently from d69e3d3 to 716a6dd Compare December 8, 2023 20:30
@brian-smith-tcril brian-smith-tcril marked this pull request as ready for review December 8, 2023 20:51
Copy link
Contributor

@PKulkoRaccoonGang PKulkoRaccoonGang left a comment

Choose a reason for hiding this comment

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

@brian-smith-tcril leave some suggestions, please take a look

src/Form/FormAutosuggest.jsx Outdated Show resolved Hide resolved
const matchingDropdownItem = filteredItems.find((o) => (
o.props.children.toLowerCase() === userProvidedText.toLowerCase()
));
if (!matchingDropdownItem) {
Copy link
Contributor

Choose a reason for hiding this comment

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

It may be worth adding spaces between constants, functions, and conditions. This will improve the readability of the code. Please let me know what your thoughts are.

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 added some spaces and comments in here, hopefully that clears things up a bit.

src/Form/form-autosuggest.mdx Outdated Show resolved Hide resolved
src/Form/tests/FormAutosuggest.test.jsx Outdated Show resolved Hide resolved
})}
value={value.userProvidedText}
/>
<Button onClick={forceUpdateErrorState}>Trigger validation</Button>
Copy link
Member

Choose a reason for hiding this comment

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

nit: consider using Stack or CSS utility class to give some vertical whitespace between the Form.Control and the Button.

image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Comment on lines 23 to 25
const [valueRequired, setValueRequired] = useState(false);
const [selectionRequired, setSelectionRequired] = useState(false);
const [customValidation, setCustomValidation] = useState(false);
Copy link
Member

Choose a reason for hiding this comment

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

nit: perhaps prefix these boolean state variables with isValueRequired, isSelectionRequired, hasCustomValidation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Comment on lines 176 to 173
useImperativeHandle(ref, () => ({
updateErrorStateAndErrorMessage,
}));
Copy link
Member

Choose a reason for hiding this comment

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

[nit/suggestion] May be a good idea to include a comment here or elsewhere documenting the use of useImperativeHandle to only include updateErrorStateAndErrorMessage on its exposed methods. That way, future contributions will have some guidance on how to keep this maintained such that methods intended to be exposed are intentionally included here? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

selectors: arrowKeyNavigationSelector,
ignoredKeys: ignoredArrowKeysNames,
});
const [dropdownExpanded, setDropdownExpanded] = useState(false);
Copy link
Member

@adamstankiewicz adamstankiewicz Jan 16, 2024

Choose a reason for hiding this comment

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

nit: isDropdownExpanded

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Comment on lines 49 to 55
const [displayValue, setDisplayValue] = useState(() => {
if (!value) {
return '';
}

setState(prevState => ({
...prevState,
dropDownItems: [],
displayValue: clickedValue,
}));
return value.userProvidedText;
});
Copy link
Member

Choose a reason for hiding this comment

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

nit: could this be simplified to:

const [displayValue, setDisplayValue] = useState(value?.userProvidedText || '');

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Comment on lines 96 to 97
// eslint-disable-next-line no-shadow
const { children, onClick, ...rest } = child.props;
Copy link
Member

Choose a reason for hiding this comment

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

nit: to avoid // eslint-disable-next-line no-shadow, consider renaming children once deconstructed (i.e., const { children: childChildren, ... } = child.props; to avoid the naming conflict with the parent children.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

if (onSelected && clickedValue !== value) {
onSelected(clickedValue);
}
const FormAutosuggest = forwardRef(
Copy link
Member

Choose a reason for hiding this comment

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

[nit/suggestion/consideration] I'm wondering if it might make sense to abstract some of this logic into custom React hook(s) to keep the component code itself more focused on the UI presentation. For example, a custom hook(s) could return the callback functions to the component for use, having been defined in the hook(s).

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'd like to think about this one a bit. I worry that too much abstraction might lead to consumers using the component in ways that feel broken, but that could likely be resolved by providing good examples.

Maybe it makes sense to break this out into a separate issue to do some discovery on it?

Copy link
Member

Choose a reason for hiding this comment

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

The suggestion to abstract some of the logic shouldn't really affect consumers? The custom hooks wouldn't be exposed to consumers; just for use internally within the component.

Generally when a components implementation reaches several hundred plus lines that feels like the component might have a bit too much going on where it could be simplified through the use of hooks to improve readability, etc.

Totally fine to defer and consider. Just a suggestion to make the component a bit more maintainable, where it makes sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The custom hooks wouldn't be exposed to consumers; just for use internally within the component.

Gotcha, I misinterpreted the comment.

Totally fine to defer and consider. Just a suggestion to make the component a bit more maintainable, where it makes sense.

That sounds good. It's definitely a good idea, but I think it makes sense to hold off until after getting this PR merged.

…values

This updates the Autosuggest component to provide the consumer with:
* The value entered in the textbox by the user
* The selected value (as it is displayed in the dropdown)
* The id of the selected option

This also allows the consumer to determine if the Autosuggest component should be in an error state when
* No text has been entered
* Text has been entered, but not selection has been made
* An external validation check has failed - "customError"

The consumer will provide error messages for the appropriate error states

BREAKING CHANGE: value of Autosuggest component is now an object instead of a string
BREAKING CHANGE: Autosuggest component now uses onChange instead of onSelected
BREAKING CHANGE: Autosuggest component now takes in different error messages for value/selection required, and custom errors
@brian-smith-tcril brian-smith-tcril changed the base branch from master to release/chip-searchfield-pagination January 16, 2024 21:46
@adamstankiewicz adamstankiewicz force-pushed the release/chip-searchfield-pagination branch from 8c09b4f to e187749 Compare January 18, 2024 17:57
@adamstankiewicz adamstankiewicz merged commit 35485ef into openedx:release/chip-searchfield-pagination Jan 18, 2024
10 checks passed
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.

6 participants