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

Update to use dmc 0.15.1 #924

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft

Update to use dmc 0.15.1 #924

wants to merge 8 commits into from

Conversation

AnnMarieW
Copy link
Contributor

@AnnMarieW AnnMarieW commented Dec 7, 2024

This is a draft PR. For details, please refer to the discussion in #905

TO DO:

  • Fix unit tests
  • Add new styling to new DatePicker @nadijagraca

Test Code

from vizro import Vizro
import vizro.models as vm
import vizro.plotly.express as px

stocks = px.data.stocks(datetimes=True)

page = vm.Page(
    title="Page",
    components=[
        vm.Graph(
            figure=px.line(stocks, x="date", y="GOOG", title="Stocks Data"),
        ),
    ],
    controls=[
        vm.Filter(column="GOOG"),
        vm.Filter(column="date", selector=vm.DatePicker(title="Date Picker (Stocks - date)")),
    ],
)

dashboard = vm.Dashboard(pages=[page])

if __name__ == "__main__":
    Vizro().build(dashboard).run()

Notice

  • I acknowledge and agree that, by checking this box and clicking "Submit Pull Request":

    • I submit this contribution under the Apache 2.0 license and represent that I am entitled to do so on behalf of myself, my employer, or relevant third parties, as applicable.
    • I certify that (a) this contribution is my original creation and / or (b) to the extent it is not my original creation, I am authorized to submit this contribution on behalf of the original creator(s) or their licensees.
    • I certify that the use of this contribution as authorized by the Apache 2.0 license does not violate the intellectual property rights of anyone else.
    • I have not referenced individuals, products or companies in any commits, directly or indirectly.
    • I have not added data or restricted code in any commits, directly or indirectly.

@AnnMarieW AnnMarieW mentioned this pull request Dec 7, 2024
Copy link
Contributor

@huong-li-nguyen huong-li-nguyen left a comment

Choose a reason for hiding this comment

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

First, thank you so much, @AnnMarieW, for this PR and your detailed explanations here! 🚀 This is incredibly helpful.

We definitely plan to upgrade the dmc version, and although we've removed other dmc components, we'll likely always keep the DatePicker since it's the best available! 💯

I've left a few comments, but there's no rush to implement them. We probably won't merge this PR until next year. We were already planning to upgrade dmc next year, but we wanted to check feasibility first. Your PR is fantastic for that! Everything seems to work fine, and it looks very feasible and straightforward from a code perspective. Thank you so much! 🙏

I'll have @nadijagraca and @petar-qb do some manual checks since they developed the component back then and probably remember better than me all the workarounds we added 😅 @nadijagraca, I'll create a separate ticket, but we need to change our CSS to match the new DatePicker CSS selectors, but that should be straight-forward hopefully!

Overall, I'm confident we can merge this PR next year though 👍 🚀

vizro-core/examples/dev/app.py Outdated Show resolved Hide resolved
vizro-core/src/vizro/_vizro.py Outdated Show resolved Hide resolved
min: Optional[date] = Field(None, description="Start date for date picker.")
max: Optional[date] = Field(None, description="End date for date picker.")
value: Optional[Union[list[date], date]] = Field(None, description="Default date for date picker")
title: str = Field("", description="Title to be displayed.")

# Could probably delete the `range` arg, but keeping it makes it backwards compatible
Copy link
Contributor

@huong-li-nguyen huong-li-nguyen Dec 12, 2024

Choose a reason for hiding this comment

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

Good point! The range argument is definitely a bit redundant if we ever want to introduce the type argument of the new dmc.DatePickerInput as well. Options would then include: "default", "range", "multiple" in which case this boolean argument doesn't make much sense as you could then also define it via the type directly, but probably better to keep it for backwards compatibility even though it might be double-defined then.

Let's leave it in for now and discuss when @antonymilne is back 👍

Copy link
Contributor

Choose a reason for hiding this comment

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

Antony previously mentioned the concept of a "multiple" option for the DatePicker in #318 (comment).

At this point, I believe we don't need to support the "multiple" mode yet, as it represents a niche use case. If we decide to implement this feature, we should also consider extending similar functionality to other components, such as vm.Slider. This would align with broader discussions about restructuring our components (See -> #318 (comment))).

For now, the range property seems to address the requirements effectively.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, perfect! I didn't know he commented on this already. Let's leave it as is then, and @AnnMarieW you could delete this line then: date_range_picker_kwargs = {"allowSingleDateInRange": True} if self.range else {}

I don't think it's needed anymore since that is controlled by the type argument now instead of allowSingleDateInRange

Copy link
Contributor

Choose a reason for hiding this comment

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

We should always include allowSingleDateInRange=True, in the dmc.DatePickerInput configuration. It will enable that a single date can be selected when range=True (so type="range"), and will have no effect when range=False (so type="default").

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah I see, but then let's specify it directly as allowSingleDateInRange=True instead of adding that line 👍

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'm a little confused by this - not sure what props to update 🤔

Copy link
Contributor

Choose a reason for hiding this comment

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

All that is needed is to delete the following line:
date_range_picker_kwargs = {"allowSingleDateInRange": True} if self.range else {}

and insert allowSingleDateInRange: True directly to the dmc.DatePickerInput configuration like in this suggestion comment.

Comment on lines 74 to 91
# clientside callback is required as a workaround when the date-picker is overflowing its parent container
# if there is not enough space. Caused by another workaround for this issue:
# https://github.com/snehilvj/dash-mantine-components/issues/219
clientside_callback(
ClientsideFunction(namespace="date_picker", function_name="update_date_picker_position"),
output=Output(self.id, "dropdownPosition"),
inputs=Input(self.id, "n_clicks"),
)

date_picker_class = dmc.DateRangePicker if self.range else dmc.DatePicker

# dropdownPosition must be set to bottom-start as a workaround for issue:
# https://github.com/snehilvj/dash-mantine-components/issues/219
# clearable must be set to False as a workaround for issue:
# https://github.com/snehilvj/dash-mantine-components/issues/212
# maxDate must be increased by one day, and later on disabledDates must be set as maxDate + 1 day
# as a workaround for issue: https://github.com/snehilvj/dash-mantine-components/issues/230
date_picker = date_picker_class(
Copy link
Contributor

Choose a reason for hiding this comment

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

Saw that all of these issues have been fixed in the mantine repository @AnnMarieW - this is amazing! 🥳

@nadijagraca @petar-qb - could you double-check manually if all of the issues we've faced initially are solved now? I remember for one of the bugs, we had to add the datepicker to the right side and then change screen sizes etc. I don't know if there were other scenarios like that which need manual checking. You will know best 👍

Copy link
Contributor

Choose a reason for hiding this comment

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

After some testing with various configuration inputs, I can say that all our previous workaround solutions are unnecessary as dmc.DatePickerInput handles them all internally 🎉 Here's the example of our workaround solutions.

  • maxDate=self.max + datetime.timedelta(days=1) if self.max else None,
  • dropdownPosition="bottom-start",
  • clearable=False,
  • disabledDates=self.max + datetime.timedelta(days=1) if self.max else None,

@AnnMarieW
Copy link
Contributor Author

Hi @huong-li-nguyen

Thanks for the review and I look forward to working on this more next year!

need to change our CSS to match the new DatePicker CSS selectors, but that should be straight-forward hopefully!

As far as styling goes -- it's changed quite substantially starting in V0.14. No need to go into details now, but a couple things to note:

  • Setting the theme prop in MantinePriverder will make it so that any custom component can also have the Vizro default themes. For example setting the theme={"primarColor: "dark"} is very close to the Vizro theme and works well in both light and dark modes. This will reduce the amount of custom CSS needed.

  • by default the calendar dropdown is rendered in a portal which is outside the dom tree. This means it can't be targeted with the component's className prop. However, it can be styled using Mantine's Style API. More on this later :-)

AnnMarieW and others added 2 commits December 12, 2024 06:13
Co-authored-by: Li Nguyen <90609403+huong-li-nguyen@users.noreply.github.com>
Copy link
Contributor

@petar-qb petar-qb left a comment

Choose a reason for hiding this comment

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

@AnnMarieW, thank you for such great work ❤️ I really like dmc.DatePickerInput!

I left a few comments, but this PR is close to being merged in my opinion 🎉

P.S. You can paste the code from the PR description you wrote directly to vizro-core/examples/scratch_dev/app.py file, and make it easier for other reviewers to test the change 😃.

vizro-core/examples/dev/app.py Outdated Show resolved Hide resolved
vizro-core/pyproject.toml Show resolved Hide resolved
vizro-core/src/vizro/_vizro.py Show resolved Hide resolved
vizro-core/src/vizro/_vizro.py Show resolved Hide resolved
min: Optional[date] = Field(None, description="Start date for date picker.")
max: Optional[date] = Field(None, description="End date for date picker.")
value: Optional[Union[list[date], date]] = Field(None, description="Default date for date picker")
title: str = Field("", description="Title to be displayed.")

# Could probably delete the `range` arg, but keeping it makes it backwards compatible
Copy link
Contributor

Choose a reason for hiding this comment

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

Antony previously mentioned the concept of a "multiple" option for the DatePicker in #318 (comment).

At this point, I believe we don't need to support the "multiple" mode yet, as it represents a niche use case. If we decide to implement this feature, we should also consider extending similar functionality to other components, such as vm.Slider. This would align with broader discussions about restructuring our components (See -> #318 (comment))).

For now, the range property seems to address the requirements effectively.

Comment on lines 74 to 91
# clientside callback is required as a workaround when the date-picker is overflowing its parent container
# if there is not enough space. Caused by another workaround for this issue:
# https://github.com/snehilvj/dash-mantine-components/issues/219
clientside_callback(
ClientsideFunction(namespace="date_picker", function_name="update_date_picker_position"),
output=Output(self.id, "dropdownPosition"),
inputs=Input(self.id, "n_clicks"),
)

date_picker_class = dmc.DateRangePicker if self.range else dmc.DatePicker

# dropdownPosition must be set to bottom-start as a workaround for issue:
# https://github.com/snehilvj/dash-mantine-components/issues/219
# clearable must be set to False as a workaround for issue:
# https://github.com/snehilvj/dash-mantine-components/issues/212
# maxDate must be increased by one day, and later on disabledDates must be set as maxDate + 1 day
# as a workaround for issue: https://github.com/snehilvj/dash-mantine-components/issues/230
date_picker = date_picker_class(
Copy link
Contributor

Choose a reason for hiding this comment

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

After some testing with various configuration inputs, I can say that all our previous workaround solutions are unnecessary as dmc.DatePickerInput handles them all internally 🎉 Here's the example of our workaround solutions.

  • maxDate=self.max + datetime.timedelta(days=1) if self.max else None,
  • dropdownPosition="bottom-start",
  • clearable=False,
  • disabledDates=self.max + datetime.timedelta(days=1) if self.max else None,

vizro-core/src/vizro/models/_dashboard.py Outdated Show resolved Hide resolved
@petar-qb
Copy link
Contributor

@AnnMarieW Can you paste the code from the PR description you wrote, directly to vizro-core/examples/scratch_dev/app.py file? This would make it easier for other reviewers to test the change 😃.

@petar-qb
Copy link
Contributor

Unit tests should not be too hard to fix. There are only three tests that are failing:

  1. test_datepicker_build (2 - for range=False and range=True) (file: test_date_picker.py),
  2. test_dashboard_build (file: test_dashboard.py)

You can fix test_datepicker_build by changing the expected_datepicker to the new form: dmc.DatePickerInput(...). Similarly, for the test_dashboard_build, all you need is to wrap the expected_dashboard_container inside the dmc.MantineProvider(expected_dashboard_container).

You can run unit tests with the following command from your terminal (ensure to call it from .../vizro/vizro-core location): hatch run test-unit.

If you need any help with this, please let us know and we will be happy to jump in for the help. 😃

@AnnMarieW
Copy link
Contributor Author

This commit is to show how to style Mantine componets with a Vizro style.

It uses the theme prop in the MantineProvider to make the default accent color gray, which works well with VIzro. This will be helpful for users adding custom Mantine components in the future.

To fine tune the datepicker, it's best to use Mantine's Style API. I deleted the datepicker.css file because most of the selectors have changed. Also, it's not possible to style the calendar with the className prop because it's rendered in a portal that is outside of the component root and inner elements are not part of the component tree. We can change it to withinPortal=False, but that can cause some issues with the z-Index.

The ideal way to style the calendar is to use Mantine Styles API. I included an example of the styles prop making all the days the same color to remove the default red for weekend days.

# maxDate must be increased by one day, and later on disabledDates must be set as maxDate + 1 day
# as a workaround for issue: https://github.com/snehilvj/dash-mantine-components/issues/230
date_picker = date_picker_class(
date_picker = dmc.DatePickerInput(
Copy link
Contributor

@petar-qb petar-qb Dec 17, 2024

Choose a reason for hiding this comment

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

This suggestion is only used as a reference from another comment.

Suggested change
date_picker = dmc.DatePickerInput(
date_picker = dmc.DatePickerInput(
id=self.id,
minDate=self.min,
value=init_value,
maxDate=self.max,
persistence=True,
persistence_type="session",
type="range" if self.range else "default",
className="datepicker",
# removes the default red color for weekend days
styles={"day": {"color": "var(--mantine-color-text"}},
allowSingleDateInRange: True,
)

@petar-qb petar-qb mentioned this pull request Dec 19, 2024
1 task
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.

3 participants