Skip to content

Conversation

@bmd3k
Copy link
Contributor

@bmd3k bmd3k commented May 3, 2023

Motivation for features / changes

We are changing single step selection to default to the maximum step. We think this is more useful than making the default the minimum step.

Technical description of changes

Change TimeSelection type so that start is nullable while end is not and adjust the code to fit.
Where default value is set to "min" step, instead set it to the "max" step.

One tricky aspect: In some places we were checking that end was falsy or truthy to determine if it was set. There was a latent bug that if end was set to 0 then it would be considered unset in those cases. This was not a big problem because end was rarely set to 0. But start is often set to 0 so those cases needed to be carefully tracked down and fixed.

Another tricky aspect: The TimeSelection refactor does not cleanly import into the internal repo. I have three CLs that accompagny this change.

  • cl/528745120 adjusts or comments out code to allow the import to succeed. It will be submitted prior to merging this PR.
  • cl/528745440 is a set of harmless screenshot-updates. It will be patched into the import CL and submitted at the same time as the import.
  • cl/528745657 is a set of changes to uncomment out the code from the first CL and make remaining adjustments. It will be submitted after the import.

Detailed steps to verify changes work correctly (as executed by you)

  • I tested it manually quite a bit.
  • I imported it into the internal repo and ensured that end-to-end tests pass (after adjusting for the change in behavior).

@bmd3k bmd3k requested a review from rileyajones May 3, 2023 14:42
export interface TimeSelection {
start: {step: number};
end: {step: number} | null;
start: {step: number} | null;
Copy link
Contributor

@rileyajones rileyajones May 3, 2023

Choose a reason for hiding this comment

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

The decision to have end be end: {step: number} | null rather than end?: {step: number} has lead to a lot of clunky code (especially in tests). I would love to see this changed and I think this may be a good time for this update. However, I recognize that this is not a small change so I'll leave it to your discretion.

Copy link
Contributor

@rileyajones rileyajones left a comment

Choose a reason for hiding this comment

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

I will get back to reviewing this tomorrow, I only made it ~50% of the way through today but I wanted to leave some initial comments for now.

This has clearly taken a lot of work and I do think it is an improvement so I'm not going to block the effort, however, given the number of PR/CLs that are going to be required to get his checked in please think carefully about the cost benefit of making such a large change.

  • Given the existing plans to remove the global values for step and range selection along with the check boxes I'm not sure how this will actually be different from the existing experience. This is definitely an improvement while the check boxes and default values stay around though.
  • The same result could likely have been achieved with only updates to the fob controller, the rangeSelectionEnabled reducer, the stepSelectionEnabled reducer, and the getCardTimeSelection selector
  • At the redux level there is always both a start and end value. This value is then clipped at the container level. You could have just removed that, removed the ability for end to be null, and updated the data table and card fob controller to read the value of getCardRangeSelectionEnabled (like they should).

};
}
if (!linkedTimeSelection.end) {
if (linkedTimeSelection.start === null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: While your solution is technically more correct I'm not sure the distinction between null and falsey is important here.

Suggested change
if (linkedTimeSelection.start === null) {
if (!linkedTimeSelection.start) {


let nextStepIndexMetaData: CardStepIndexMetaData | null = null;
if (timeSelection.end === null) {
if (timeSelection.start === null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit

Suggested change
if (timeSelection.start === null) {
if (!timeSelection.start) {

if (steps.indexOf(timeSelection.start.step) !== -1)
return [timeSelection.start.step];
// Single selection: returns end step if matching any step in the list, otherwise returns nothing.
if (timeSelection.start === null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit

Suggested change
if (timeSelection.start === null) {
if (!timeSelection.start) {

Comment on lines 70 to 73
@Input() timeSelection!: {
start: {step: number};
end: {step: number} | null;
start: {step: number} | null;
end: {step: number};
} | null;
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm aware that you are just updating the existing code but it would be nice to actually use the type here

Suggested change
@Input() timeSelection!: {
start: {step: number};
end: {step: number} | null;
start: {step: number} | null;
end: {step: number};
} | null;
@Input() timeSelection!: TimeSelection | null`

(step) =>
step >= linkedTimeSelection.startStep &&
step <= linkedTimeSelection.endStep!
step >= linkedTimeSelection.startStep! &&
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a rare situation where the difference between null and undefined actually matters. If you do decide to take my advice to change to using undefined rather than null you will need to this.
For reference numbers are never greater than nor less than undefined but null is treated as 0.

]).pipe(
map(([timeSelection, singleSelectionHeaders, rangeSelectionHeaders]) => {
if (!timeSelection || timeSelection.end === null) {
if (!timeSelection || timeSelection.start === null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit

Suggested change
if (!timeSelection || timeSelection.start === null) {
if (!timeSelection || !timeSelection.start) {

return [timeSelection.start.step];
// Single selection: returns end step if matching any step in the list, otherwise returns nothing.
if (timeSelection.start === null) {
if (steps.indexOf(timeSelection.end.step) !== -1)
Copy link
Contributor

Choose a reason for hiding this comment

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

Because we are not actually using the index this seems like an excellent place to use the includes function

Suggested change
if (steps.indexOf(timeSelection.end.step) !== -1)
if (steps.includes(timeSelection.end.step))

@bmd3k
Copy link
Contributor Author

bmd3k commented May 8, 2023

This has clearly taken a lot of work and I do think it is an improvement so I'm not going to block the effort, however, given the number of PR/CLs that are going to be required to get his checked in please think carefully about the cost benefit of making such a large change.

Good advice. I will close this and have a simpler change in #6372.

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.

2 participants