-
-
Notifications
You must be signed in to change notification settings - Fork 735
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
Replace dateLib object with DateLib class #2538
Conversation
89feae9
to
9878cf7
Compare
Include locale, weekStartsOn, firstWeekContainsDate, useAdditionalWeekYearTokens, useAdditionalDayOfYearTokens formatting props in DateLib class
9878cf7
to
ef6ca8b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work here, @daveallie! I’ll need some time to review it. Thanks...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find your solution very interesting, and there's definitely something to learn from your changes. I'm not very up-to-date with OOP in JavaScript, so here are some initial thoughts on DateLib
.
The thing is, we should also test it, and I'm trying to find a way to simplify the code.
@@ -437,7 +437,7 @@ export interface PropsBase { | |||
* @since 9.0.0 | |||
* @experimental | |||
*/ | |||
dateLib?: Partial<DateLib> | undefined; | |||
dateLib?: Partial<DateLibBase> | undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand your decision to name the class DateLib
and the type DateLibBase
, but I find it somewhat confusing. As a consumer, I would expect dateLib
to be a partial of a type called DateLib
. Additionally, this would introduce a breaking change. ...exploring ways to keep the type name unchanged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah fair, I can leave the name unchanged for exporting, and adjust the internal name from DateLib
to DateLibImpl
or something
* | ||
* @param options The formatting options for the date library. | ||
*/ | ||
static fromOptionsDefaultLocale( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to avoid using this static method? It’s a bit confusing to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, let me take another look at my approach, will attempt to rework this part
* @private | ||
* @internal | ||
*/ | ||
export class DateLib { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this work somehow?
export class DateLib { | |
export class DateLib extends DefaultDateLib { |
this.dateLibBase = { | ||
...defaultDateLibBase, | ||
...dateLibBaseOverrides | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the dateLibBase
property necessary? What about a DateLib
class to extends a DefaultDateLib
: Then, in the constructor:
if ('addWeeks' in dateLibBaseOverrides) this.addWeeks = dateLibBaseOverrides.addWeeks;
There might be reasons why you’re not doing this, but I can’t identify any just by looking at the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
currently I've done it this way, as for endOfWeek
, format
, getWeek
, and startOfWeek
(locale required functions), I still want access to the original function with the correct name, but the interface from the class to not include the locale and have it pass in the internally known locale to the original function. I'll play around with another way to achieve the same thing a little cleaner.
if (!this.locale) { | ||
throw new Error( | ||
"DateLib needs to be initialized with locale to use the `format` function." | ||
); | ||
} | ||
|
||
return format(date, formatStr, this.options); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The assumption here is that this.options
includes this.locale
, right? If so, why is this.locale
needed separately? Couldn't we just:
if (!this.locale) { | |
throw new Error( | |
"DateLib needs to be initialized with locale to use the `format` function." | |
); | |
} | |
return format(date, formatStr, this.options); | |
}; | |
if (!this.options.locale) { | |
throw new Error( | |
"DateLib needs to be initialized with locale to use the `format` function." | |
); | |
} | |
return format(date, formatStr, this.options); | |
}; |
P.S. I love how well the code is documented. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah absolutely! I think what you're suggesting is more clear, I was just using this.locale
as shorthand, but it's more indirection.
throw new Error( | ||
"DateLib needs to be initialized with locale to use the `getWeek` function." | ||
); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, can’t we rely on a default this.options.locale
to avoid these checks? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Default locale is what caused the original bug, by making locale optional on deeper calls in the library, we're allowing callers to forget to provide the known locale. For this implementation, I took the approach of still allowing locale optionally, but ensuring it's provided to use any function which is dependent on locale. This should help catch issues where locale is forgotten about in DateLib construction, but required when rendering.
Ultimately DateLib construction only happens inside react-day-picker
, so the only time this is hit will be if we've written a bug, it's a footgun prevention mechanism
Thanks for the review @gpbl, I'm not an expert with OOP in JS either tbh, but it's pretty powerful if done right and this seems like a good use for it (wrapping up context and logic into a single instance). I had to wrap my head around the default args in the renderers which were a bit squirrelly, but this approach functions fine and provides a little more robust-ness for future expansion and prevention of issues in the future. I'll throw up another revision in another few days addressing your feedback, if you spot anything else in there you'd like changed, please call it out. 🫡 |
@daveallie, I had the chance to review the You can check out the code here: dateLib.ts. I still need to add tests, particularly for the weekday names that you met before, but this version is closer to the concept I had in mind. If you have some time, I’d appreciate a quick review! |
PS. I tried configuring my PR to merge the changes into this one (for an easier diff), but for some reason, I couldn't get it to work... |
@daveallie thanks so much for your extensive work here! I've merged #2550 which includes your work here and the test for #2511. |
Thanks for picking this up and following it through @gpbl, appreciate it 🤝 |
What's Changed
dateLib
was an object containing a subset of date-fns functions, it is now a full class, which takes that subset and locale/formatting options as constructor arguments.DateLib
can take an undefined Locale, functions withinDateLib
which different return values based on locale, will require the DateLib class to have been initialised with a locale in order to be called.weekStartsOn
,firstWeekContainsDate
,useAdditionalWeekYearTokens
,useAdditionalDayOfYearTokens
are all passed toDateLib
on initialization and explicitly not allowed to be passed in directly when calling those functions, instead we use the values that DateLib was initialized withdateLib
as it was used beforedateLib.options
exposes the formatting options it was initialized with, allowing for them to be passed down to user-provided label/format functions.Type of Change
Additional Notes
Fixes #2509
Related #2523
Related #2511