From 9e1350e22f286ead5a735e0c4dc6623f530f9806 Mon Sep 17 00:00:00 2001 From: Laura Griffee Date: Fri, 26 May 2023 13:33:28 -0600 Subject: [PATCH] @shopify/polaris v11.0.0 major release (#7597) ## @shopify/polaris v11.0.0 ### Dependencies - [x] https://github.com/Shopify/polaris/pull/8200 ### NodeJS - [x] https://github.com/Shopify/polaris/pull/8201 ### TypeScript - [x] https://github.com/Shopify/polaris/pull/8203 ### Components - [x] https://github.com/Shopify/polaris/pull/7349 - [x] https://github.com/Shopify/polaris/pull/7397 - [x] https://github.com/Shopify/polaris/pull/7962 - [x] https://github.com/Shopify/polaris/pull/8187 - [x] https://github.com/Shopify/polaris/pull/8184 - [x] https://github.com/Shopify/polaris/pull/8206 - [x] https://github.com/Shopify/polaris/pull/7990 - [x] https://github.com/Shopify/polaris/pull/8468 - [x] https://github.com/Shopify/polaris/pull/8577 - [x] https://github.com/Shopify/polaris/pull/8631 - [x] https://github.com/Shopify/polaris/pull/8962 ## @shopify/polaris-tokens v7.0.0 ### Tokens - [x] https://github.com/Shopify/polaris/issues/6920 - [x] https://github.com/Shopify/polaris/pull/8245 - [x] https://github.com/Shopify/polaris/issues/4826 - [x] https://github.com/Shopify/polaris/issues/8405 ## @shopify/stylelint-polaris v7.0.0 - [x] https://github.com/Shopify/polaris/issues/7622 - [x] https://github.com/Shopify/polaris/issues/8419 # Post @shopify/polaris v11 shipping - [ ] https://github.com/Shopify/polaris/issues/8420 ## Low priority or not ready breaking changes - [x] Remove deprecated layout components - [x] Release Layout primitive components --------- Co-authored-by: Tim Layton Co-authored-by: Ryan Musgrave Co-authored-by: Ryan Musgrave Co-authored-by: aveline Co-authored-by: Kyle Durand Co-authored-by: Matt Gregg Co-authored-by: Alex Page Co-authored-by: Lo Kim Co-authored-by: Ben Scott <227292+BPScott@users.noreply.github.com> Co-authored-by: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> Co-authored-by: Sam Rose <11774595+samrose3@users.noreply.github.com> Co-authored-by: Sam Rose Co-authored-by: Marc Thomas Co-authored-by: Alex Page <19199063+alex-page@users.noreply.github.com> Co-authored-by: Chloe Rice <18447883+chloerice@users.noreply.github.com> Co-authored-by: Chloe Rice Co-authored-by: Joe Thomas Co-authored-by: Yuraima Estevez Co-authored-by: shopify-github-actions-access[bot] <109624739+shopify-github-actions-access[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] --- .changeset/afraid-scissors-work.md | 6 + .changeset/cool-starfishes-cry.md | 7 + .changeset/few-forks-change.md | 5 + .changeset/four-tables-hide.md | 5 + .changeset/great-cameras-matter.md | 6 + .changeset/light-mails-type.md | 6 + .changeset/light-steaks-promise.md | 5 + .changeset/metal-spies-serve.md | 5 + .changeset/nasty-suns-hang.md | 5 + .changeset/quiet-toes-play.md | 12 + .changeset/real-lemons-sniff.md | 6 + .changeset/rude-teachers-doubt.md | 5 + .changeset/six-jokes-drum.md | 6 + .changeset/slimy-countries-learn.md | 5 + .changeset/stupid-vans-kick.md | 5 + .eslintrc.js | 6 - .github/CODEOWNERS | 18 - .github/CONTRIBUTING.md | 2 +- .github/workflows/changelog.yml | 4 +- .github/workflows/ci-a11y-vrt.yml | 8 +- .github/workflows/ci.yml | 2 +- .github/workflows/size-limit.yml | 45 - .gitignore | 1 + .nvmrc | 2 +- babel.config.js | 66 +- dev.yml | 2 +- documentation/Nodejs support.md | 18 +- .../guides/migrating-from-v10-to-v11.md | 1237 ++++++++++++++ package.json | 35 +- polaris-cli/package.json | 5 +- .../v9-scss-replace-color/transform.ts | 3 +- .../v10-legacy-colors.ts | 170 +- polaris-for-vscode/package.json | 3 +- polaris-for-vscode/src/server.ts | 8 +- polaris-icons/package.json | 3 + polaris-icons/rollup.config.mjs | 20 +- polaris-migrator/package.json | 3 + polaris-migrator/rollup.config.mjs | 6 +- .../v10-legacy-colors.ts | 647 ++++++++ .../v9-scss-replace-color.ts | 4 +- polaris-react/package.json | 28 +- polaris-react/playground/DetailsPage.tsx | 5 +- polaris-react/rollup.config.mjs | 9 +- polaris-react/scripts/build-validate.js | 14 +- .../AccountConnection/AccountConnection.tsx | 6 +- .../ActionList/components/Item/Item.tsx | 2 +- .../AlphaCard/AlphaCard.stories.tsx | 102 -- .../src/components/AlphaCard/AlphaCard.tsx | 59 - .../src/components/AlphaCard/index.ts | 1 - .../AlphaCard/tests/AlphaCard.test.tsx | 46 - .../components/AlphaFilters/AlphaFilters.scss | 235 --- .../AlphaFilters/AlphaFilters.stories.tsx | 1453 ----------------- .../components/AlphaFilters/AlphaFilters.tsx | 444 ----- .../AlphaFilters/components/index.ts | 2 - .../src/components/AlphaFilters/index.ts | 2 - .../AlphaFilters/tests/AlphaFilters.test.tsx | 324 ---- .../src/components/AlphaTabs/AlphaTabs.scss | 306 ---- .../AlphaTabs/AlphaTabs.stories.tsx | 279 ---- .../src/components/AlphaTabs/AlphaTabs.tsx | 639 -------- .../AlphaTabs/components/Item/Item.tsx | 64 - .../AlphaTabs/components/Item/index.ts | 1 - .../components/Item/tests/Item.test.tsx | 28 - .../AlphaTabs/components/List/List.tsx | 51 - .../AlphaTabs/components/List/index.ts | 1 - .../components/List/tests/List.test.tsx | 111 -- .../AlphaTabs/components/Panel/Panel.tsx | 26 - .../AlphaTabs/components/Panel/index.ts | 1 - .../components/Panel/tests/Panel.test.tsx | 31 - .../AlphaTabs/components/Tab/Tab.tsx | 429 ----- .../AlphaTabs/components/Tab/index.ts | 1 - .../components/Tab/tests/Tab.test.tsx | 359 ---- .../components/TabMeasurer/TabMeasurer.tsx | 97 -- .../AlphaTabs/components/TabMeasurer/index.ts | 1 - .../TabMeasurer/tests/TabMeasurer.test.tsx | 153 -- .../components/AlphaTabs/components/index.ts | 6 - .../src/components/AlphaTabs/index.ts | 2 - .../AlphaTabs/tests/AlphaTabs.test.tsx | 660 -------- .../src/components/AlphaTabs/types.ts | 72 - .../src/components/AlphaTabs/utilities.ts | 47 - .../components/AppProvider/AppProvider.scss | 3 - .../AppProvider/AppProvider.stories.tsx | 2 +- .../components/AppProvider/AppProvider.tsx | 31 +- .../src/components/Banner/Banner.scss | 2 - .../src/components/Bleed/Bleed.stories.tsx | 13 +- .../components/Breadcrumbs/Breadcrumbs.tsx | 33 +- .../Breadcrumbs/tests/Breadcrumbs.test.tsx | 96 +- .../src/components/Button/Button.scss | 4 - .../src/components/Caption/Caption.scss | 15 - .../components/Caption/Caption.stories.tsx | 20 - .../src/components/Caption/Caption.tsx | 19 - polaris-react/src/components/Caption/index.ts | 1 - .../components/Caption/tests/Caption.test.tsx | 18 - polaris-react/src/components/Card/Card.scss | 165 -- .../src/components/Card/Card.stories.tsx | 416 +---- polaris-react/src/components/Card/Card.tsx | 187 +-- .../Card/components/Header/Header.tsx | 49 - .../Card/components/Header/index.ts | 1 - .../components/Header/tests/Header.test.tsx | 75 - .../Card/components/Section/Section.tsx | 80 - .../Card/components/Section/index.ts | 1 - .../components/Section/tests/Section.test.tsx | 90 - .../Card/components/Subsection/Subsection.tsx | 19 - .../Card/components/Subsection/index.ts | 1 - .../Subsection/tests/Subsection.test.tsx | 16 - .../src/components/Card/components/index.ts | 5 - .../src/components/Card/tests/Card.test.tsx | 253 +-- .../CheckableButton/CheckableButton.scss | 2 - .../src/components/Checkbox/Checkbox.tsx | 5 +- .../Checkbox/tests/Checkbox.test.tsx | 8 +- .../src/components/ChoiceList/ChoiceList.tsx | 6 +- .../components/Collapsible/Collapsible.tsx | 3 - .../components/TextField/TextField.tsx | 5 +- .../components/DisplayText/DisplayText.scss | 55 - .../DisplayText/DisplayText.stories.tsx | 23 - .../components/DisplayText/DisplayText.tsx | 43 - .../src/components/DisplayText/index.ts | 1 - .../DisplayText/tests/DisplayText.test.tsx | 34 - .../components/DropZone/DropZone.stories.tsx | 22 +- .../src/components/DropZone/DropZone.tsx | 13 +- .../src/components/Filters/Filters.scss | 334 ++-- .../components/Filters/Filters.stories.tsx | 244 +-- .../src/components/Filters/Filters.tsx | 954 +++++------ .../ConnectedFilterControl.scss | 111 -- .../ConnectedFilterControl.tsx | 258 --- .../components/Item/Item.tsx | 29 - .../components/Item/index.ts | 1 - .../components/Item/tests/Item.test.tsx | 26 - .../components/index.ts | 1 - .../ConnectedFilterControl/index.ts | 1 - .../tests/ConnectedFilterControl.test.tsx | 263 --- .../components/FilterPill/FilterPill.scss | 8 +- .../components/FilterPill/FilterPill.tsx | 0 .../components/FilterPill/index.ts | 0 .../FilterPill/tests/FilterPill.test.tsx | 0 .../components/SearchField/SearchField.tsx | 0 .../components/SearchField/index.ts | 0 .../SearchField/tests/SearchField.test.tsx | 0 .../components/TagsWrapper/TagsWrapper.tsx | 20 - .../Filters/components/TagsWrapper/index.ts | 1 - .../TagsWrapper/tests/TagsWrapper.test.tsx | 35 - .../components/Filters/components/index.ts | 4 +- polaris-react/src/components/Filters/index.ts | 1 + .../components/Filters/tests/Filters.test.tsx | 951 +++-------- .../src/components/FooterHelp/FooterHelp.scss | 2 - .../FormLayout/components/Group/Group.tsx | 5 +- polaris-react/src/components/Frame/Frame.scss | 2 - polaris-react/src/components/Frame/Frame.tsx | 2 +- .../src/components/Heading/Heading.scss | 18 - .../components/Heading/Heading.stories.tsx | 11 - .../src/components/Heading/Heading.tsx | 32 - polaris-react/src/components/Heading/index.ts | 1 - .../components/Heading/tests/Heading.test.tsx | 34 - .../IndexFilters/IndexFilters.stories.tsx | 12 +- .../components/IndexFilters/IndexFilters.tsx | 18 +- .../IndexFilters/tests/IndexFilters.test.tsx | 16 +- .../src/components/IndexTable/IndexTable.tsx | 2 +- .../src/components/KonamiCode/KonamiCode.tsx | 50 - .../src/components/KonamiCode/index.ts | 1 - .../KonamiCode/tests/KonamiCode.test.tsx | 54 - .../src/components/LegacyCard/LegacyCard.scss | 8 - .../src/components/Listbox/Listbox.tsx | 4 +- .../Listbox/components/Option/Option.tsx | 5 +- .../Listbox/components/Section/Section.tsx | 9 +- polaris-react/src/components/Modal/Modal.tsx | 5 +- .../Modal/components/Dialog/Dialog.tsx | 2 +- .../src/components/Navigation/Navigation.scss | 4 +- .../Navigation/components/Item/Item.tsx | 5 +- .../Item/components/Secondary/Secondary.tsx | 5 +- .../Secondary/tests/Secondary.test.tsx | 2 +- .../components/Item/tests/Item.test.tsx | 2 +- .../Navigation/components/Section/Section.tsx | 5 +- .../src/components/OptionList/OptionList.tsx | 6 +- .../components/Checkbox/Checkbox.tsx | 6 +- .../src/components/Page/Page.stories.tsx | 21 +- polaris-react/src/components/Page/Page.tsx | 4 - .../Page/components/Header/Header.tsx | 39 +- .../components/Header/tests/Header.test.tsx | 32 +- .../src/components/Page/tests/Page.test.tsx | 39 +- .../PolarisTestProvider.tsx | 38 +- .../src/components/Popover/Popover.tsx | 4 +- .../PopoverOverlay/PopoverOverlay.tsx | 2 +- .../components/Popover/tests/Popover.test.tsx | 4 +- .../src/components/Portal/Portal.tsx | 5 +- .../components/Portal/tests/Portal.test.tsx | 6 +- .../components/ProgressBar/ProgressBar.tsx | 4 +- .../components/RadioButton/RadioButton.tsx | 6 +- .../components/RangeSlider/RangeSlider.tsx | 6 +- .../components/ResourceItem/ResourceItem.tsx | 60 +- .../components/ResourceList/ResourceList.tsx | 2 +- .../components/ScrollTo/ScrollTo.tsx | 5 +- .../src/components/Select/Select.scss | 2 - .../src/components/Select/Select.tsx | 6 +- .../SettingToggle/SettingToggle.stories.tsx | 10 +- .../SettingToggle/SettingToggle.tsx | 8 +- polaris-react/src/components/Sheet/Sheet.tsx | 2 +- .../components/SkeletonPage/SkeletonPage.tsx | 24 +- .../SkeletonPage/tests/SkeletonPage.test.tsx | 4 +- polaris-react/src/components/Stack/Stack.scss | 120 -- .../src/components/Stack/Stack.stories.tsx | 96 -- polaris-react/src/components/Stack/Stack.tsx | 82 - .../components/Stack/components/Item/Item.tsx | 28 - .../components/Stack/components/Item/index.ts | 1 - .../src/components/Stack/components/index.ts | 1 - polaris-react/src/components/Stack/index.ts | 1 - .../src/components/Stack/tests/Stack.test.tsx | 16 - .../src/components/Subheading/Subheading.scss | 19 - .../Subheading/Subheading.stories.tsx | 11 - .../src/components/Subheading/Subheading.tsx | 34 - .../src/components/Subheading/index.ts | 1 - .../Subheading/tests/Subheading.test.tsx | 46 - polaris-react/src/components/Tabs/Tabs.scss | 299 ++-- .../src/components/Tabs/Tabs.stories.tsx | 187 ++- polaris-react/src/components/Tabs/Tabs.tsx | 889 ++++++---- .../CreateViewModal/CreateViewModal.tsx | 0 .../components/CreateViewModal/index.ts | 0 .../tests/CreateViewModal.test.tsx | 0 .../components/Tabs/components/Item/Item.tsx | 108 +- .../components/Tabs/components/List/List.tsx | 19 +- .../Tabs/components/List/tests/List.test.tsx | 7 + .../components/Tabs/components/Tab/Tab.tsx | 511 ++++-- .../DuplicateModal/DuplicateModal.tsx | 0 .../Tab/components/DuplicateModal/index.ts | 0 .../tests/DuplicateModal.test.tsx | 0 .../components/RenameModal/RenameModal.tsx | 0 .../Tab/components/RenameModal/index.ts | 0 .../RenameModal/tests/RenameModal.test.tsx | 0 .../components/Tab/components/index.ts | 0 .../components/Tabs/components/Tab/index.ts | 2 +- .../Tabs/components/Tab/tests/Tab.test.tsx | 404 ++++- .../components/TabMeasurer/TabMeasurer.tsx | 36 +- .../TabMeasurer/tests/TabMeasurer.test.tsx | 3 + .../src/components/Tabs/components/index.ts | 11 +- polaris-react/src/components/Tabs/index.ts | 1 + .../src/components/Tabs/tests/Tabs.test.tsx | 367 +++-- polaris-react/src/components/Tabs/types.ts | 74 +- .../src/components/Tabs/utilities.ts | 4 +- .../src/components/TextField/TextField.scss | 2 - .../src/components/TextField/TextField.tsx | 6 +- .../TextField/tests/TextField.test.tsx | 10 +- .../src/components/TextStyle/TextStyle.scss | 47 - .../TextStyle/TextStyle.stories.tsx | 37 - .../src/components/TextStyle/TextStyle.tsx | 51 - .../src/components/TextStyle/index.ts | 1 - .../TextStyle/tests/TextStyle.test.tsx | 64 - polaris-react/src/components/Toast/Toast.tsx | 5 +- .../src/components/Tooltip/Tooltip.tsx | 5 +- .../components/SearchField/SearchField.tsx | 5 +- .../VisuallyHidden/VisuallyHidden.scss | 6 - .../VisuallyHidden/VisuallyHidden.stories.tsx | 68 - .../VisuallyHidden/VisuallyHidden.tsx | 19 - .../src/components/VisuallyHidden/index.ts | 1 - polaris-react/src/configure.ts | 17 - polaris-react/src/index.ts | 48 +- .../src/styles/shared/_typography.scss | 3 - .../src/utilities/focus-manager/hooks.ts | 5 +- .../focus-manager/tests/hooks.test.tsx | 14 +- .../src/utilities/unique-id/context.ts | 7 - .../src/utilities/unique-id/hooks.ts | 39 - .../src/utilities/unique-id/index.ts | 5 - .../utilities/unique-id/tests/hooks.test.tsx | 199 --- .../unique-id/tests/unique-id-factory.test.ts | 36 - .../utilities/unique-id/unique-id-factory.ts | 25 - polaris-react/tsconfig.json | 2 +- polaris-tokens/README.md | 8 +- polaris-tokens/package.json | 16 +- polaris-tokens/rollup.config.mjs | 4 +- polaris-tokens/src/index.ts | 47 +- polaris-tokens/src/metadata.ts | 14 +- polaris-tokens/src/token-groups/depth.ts | 67 - polaris-tokens/src/token-groups/legacy.ts | 66 - polaris-tokens/src/token-groups/motion.ts | 84 - polaris-tokens/src/token-groups/shape.ts | 100 -- polaris-tokens/src/token-groups/spacing.ts | 82 - polaris-tokens/src/token-groups/zIndex.ts | 38 +- polaris-tokens/src/utilities.ts | 2 +- polaris-tokens/tests/toStyleSheet.test.js | 10 +- polaris-tokens/tests/utilities.test.js | 12 +- .../content/components/deprecated/caption.md | 37 - .../content/components/deprecated/card.md | 237 --- .../components/deprecated/display-text.md | 42 - .../content/components/deprecated/heading.md | 44 +- .../content/components/deprecated/stack.md | 81 - .../components/deprecated/subheading.md | 43 - .../components/deprecated/text-container.md | 2 +- .../components/deprecated/text-style.md | 32 - .../components/deprecated/visually-hidden.md | 17 - .../components/feedback-indicators/banner.md | 2 +- .../feedback-indicators/skeleton-body-text.md | 2 +- .../components/layout-and-structure/box.md | 2 +- .../layout-and-structure/callout-card.md | 2 +- .../{alpha-card.md => card.md} | 14 +- .../components/layout-and-structure/grid.md | 2 +- .../components/layout-and-structure/index.md | 2 +- .../components/layout-and-structure/layout.md | 6 +- .../layout-and-structure/legacy-card.md | 2 +- .../layout-and-structure/media-card.md | 2 +- .../components/layout-and-structure/page.md | 2 +- .../layout-and-structure/vertical-stack.md | 2 +- .../content/components/lists/list.md | 2 +- .../components/navigation/alpha-tabs.md | 80 - .../content/components/navigation/tabs.md | 6 + .../selection-and-input/alpha-filters.md | 148 -- .../components/selection-and-input/filters.md | 8 +- .../content/components/typography/text.md | 3 - .../components/utilities/collapsible.md | 2 +- .../components/utilities/scrollable.md | 2 +- .../app-settings-layout/variants/default.md | 10 +- .../date-picking/variants/single-date.md | 6 +- .../variants/default.md | 22 +- .../resource-index-layout/variants/default.md | 2 +- .../content/whats-new/version-11-color.md | 80 + .../content/whats-new/version-11-layout.md | 54 + polaris.shopify.com/next.config.js | 18 +- polaris.shopify.com/package.json | 6 +- .../pages/api/search/v0/index.tsx | 4 +- .../components/[group]/[component]/index.tsx | 1 + .../pages/examples/alpha-filters-disabled.tsx | 145 -- .../alpha-filters-with-a-data-table.tsx | 194 --- .../alpha-filters-with-a-resource-list.tsx | 228 --- .../alpha-filters-with-children-content.tsx | 150 -- .../alpha-filters-with-query-field-hidden.tsx | 237 --- .../pages/examples/alpha-tabs-default.tsx | 33 - .../pages/examples/alpha-tabs-fitted.tsx | 43 - .../examples/alpha-tabs-inside-of-a-card.tsx | 35 - .../examples/alpha-tabs-with-actions.tsx | 77 - .../alpha-tabs-with-custom-disclosure.tsx | 68 - .../app-provider-with-link-component.tsx | 2 +- ...lpha-card-default.tsx => card-default.tsx} | 10 +- ...ners.tsx => card-with-rounded-corners.tsx} | 10 +- ...d.tsx => card-with-subdued-background.tsx} | 10 +- ...ding.tsx => card-with-varying-padding.tsx} | 22 +- .../examples/deprecated-card-default.tsx | 8 +- ...deprecated-card-with-a-subdued-section.tsx | 24 - .../deprecated-card-with-all-elements.tsx | 98 -- ...ecated-card-with-custom-footer-actions.tsx | 27 - ...ated-card-with-custom-react-node-title.tsx | 28 - ...eprecated-card-with-destructive-action.tsx | 21 - ...ed-card-with-destructive-footer-action.tsx | 22 - .../deprecated-card-with-flushed-sections.tsx | 25 - .../deprecated-card-with-footer-actions.tsx | 22 - .../deprecated-card-with-header-actions.tsx | 16 - ...ated-card-with-multiple-footer-actions.tsx | 25 - ...deprecated-card-with-multiple-sections.tsx | 21 - ...ted-card-with-multiple-titled-sections.tsx | 21 - ...recated-card-with-sections-and-actions.tsx | 18 - .../deprecated-card-with-separate-header.tsx | 38 - ...ard-with-subdued-for-secondary-content.tsx | 16 - .../deprecated-card-with-subsection.tsx | 36 - .../examples/drop-zone-with-drop-on-page.tsx | 2 +- .../pages/examples/filters-disabled.tsx | 13 +- .../examples/filters-with-a-data-table.tsx | 24 +- .../examples/filters-with-a-resource-list.tsx | 43 +- .../filters-with-children-content.tsx | 5 +- ... => filters-with-query-field-disabled.tsx} | 15 +- .../filters-with-query-field-hidden.tsx | 51 +- ...led.tsx => filters-with-some-disabled.tsx} | 25 +- .../pages/examples/index-filters-default.tsx | 4 +- .../pages/examples/index-filters-disabled.tsx | 4 +- .../index-filters-with-filtering-mode.tsx | 4 +- .../index-filters-with-no-filters.tsx | 4 +- ...ndex-filters-with-no-search-or-filters.tsx | 4 +- .../examples/index-filters-with-no-search.tsx | 4 +- .../index-filters-with-pinned-filters.tsx | 4 +- ...ensed-with-views-search-filter-sorting.tsx | 13 +- .../examples/index-table-with-filtering.tsx | 4 +- .../index-table-with-loading-state.tsx | 4 +- ...table-with-views-search-filter-sorting.tsx | 4 +- .../legacy-card-with-all-elements.tsx | 3 +- .../pages/examples/page-default.tsx | 2 +- .../pages/examples/page-narrow-width.tsx | 2 +- .../page-with-content-after-title.tsx | 2 +- .../page-with-custom-primary-action.tsx | 2 +- .../pages/examples/page-with-divider.tsx | 6 +- .../pages/examples/page-with-subtitle.tsx | 2 +- .../examples/page-without-pagination.tsx | 2 +- .../page-without-primary-action-in-header.tsx | 2 +- ...tting-toggle-with-primitive-components.tsx | 6 +- .../pages/examples/stack-default.tsx | 16 - ...ck-fill-available-space-proportionally.tsx | 17 - .../pages/examples/stack-non-wrapping.tsx | 16 - .../pages/examples/stack-spacing.tsx | 14 - .../examples/stack-vertical-centering.tsx | 21 - ...-single-item-fills-the-remaining-space.tsx | 23 - .../stack-where-items-fill-space-evenly.tsx | 17 - .../pages/examples/tabs-default.tsx | 16 +- .../pages/examples/tabs-fitted.tsx | 4 +- ...-content.tsx => tabs-inside-of-a-card.tsx} | 35 +- .../pages/examples/tabs-with-actions.tsx | 101 ++ .../examples/tabs-with-badge-content.tsx | 16 +- .../images/components/deprecated/stack.png | Bin 8562 -> 0 bytes .../layout-and-structure/alpha-card.png | Bin 16995 -> 0 bytes .../card.png | Bin .../components/navigation/alpha-tabs.png | Bin 17496 -> 0 bytes .../images/components/navigation/tabs.png | Bin 19758 -> 17496 bytes .../selection-and-input/alpha-filters.png | Bin 18427 -> 0 bytes .../selection-and-input/filters.png | Bin 19754 -> 18427 bytes .../public/images/updates/alias-tokens.png | Bin 0 -> 219078 bytes .../images/updates/new-colors-cover.png | Bin 0 -> 184148 bytes .../public/images/updates/new-palette.png | Bin 0 -> 40266 bytes .../public/images/updates/old-palette.png | Bin 0 -> 32859 bytes .../scripts/get-props/src/get-props.ts | 5 + .../WhatsNewListing.module.scss | 1 + stylelint-polaris/package.json | 3 + yarn.lock | 635 +++---- 404 files changed, 6366 insertions(+), 16490 deletions(-) create mode 100644 .changeset/afraid-scissors-work.md create mode 100644 .changeset/cool-starfishes-cry.md create mode 100644 .changeset/few-forks-change.md create mode 100644 .changeset/four-tables-hide.md create mode 100644 .changeset/great-cameras-matter.md create mode 100644 .changeset/light-mails-type.md create mode 100644 .changeset/light-steaks-promise.md create mode 100644 .changeset/metal-spies-serve.md create mode 100644 .changeset/nasty-suns-hang.md create mode 100644 .changeset/quiet-toes-play.md create mode 100644 .changeset/real-lemons-sniff.md create mode 100644 .changeset/rude-teachers-doubt.md create mode 100644 .changeset/six-jokes-drum.md create mode 100644 .changeset/slimy-countries-learn.md create mode 100644 .changeset/stupid-vans-kick.md delete mode 100644 .github/CODEOWNERS delete mode 100644 .github/workflows/size-limit.yml create mode 100644 documentation/guides/migrating-from-v10-to-v11.md rename polaris-tokens/src/token-groups/colors.ts => polaris-codemods/src/codemods/v9-scss-replace-color/v10-legacy-colors.ts (84%) create mode 100644 polaris-migrator/src/migrations/v9-scss-replace-color/v10-legacy-colors.ts delete mode 100644 polaris-react/src/components/AlphaCard/AlphaCard.stories.tsx delete mode 100644 polaris-react/src/components/AlphaCard/AlphaCard.tsx delete mode 100644 polaris-react/src/components/AlphaCard/index.ts delete mode 100644 polaris-react/src/components/AlphaCard/tests/AlphaCard.test.tsx delete mode 100644 polaris-react/src/components/AlphaFilters/AlphaFilters.scss delete mode 100644 polaris-react/src/components/AlphaFilters/AlphaFilters.stories.tsx delete mode 100644 polaris-react/src/components/AlphaFilters/AlphaFilters.tsx delete mode 100644 polaris-react/src/components/AlphaFilters/components/index.ts delete mode 100644 polaris-react/src/components/AlphaFilters/index.ts delete mode 100644 polaris-react/src/components/AlphaFilters/tests/AlphaFilters.test.tsx delete mode 100644 polaris-react/src/components/AlphaTabs/AlphaTabs.scss delete mode 100644 polaris-react/src/components/AlphaTabs/AlphaTabs.stories.tsx delete mode 100644 polaris-react/src/components/AlphaTabs/AlphaTabs.tsx delete mode 100644 polaris-react/src/components/AlphaTabs/components/Item/Item.tsx delete mode 100644 polaris-react/src/components/AlphaTabs/components/Item/index.ts delete mode 100644 polaris-react/src/components/AlphaTabs/components/Item/tests/Item.test.tsx delete mode 100644 polaris-react/src/components/AlphaTabs/components/List/List.tsx delete mode 100644 polaris-react/src/components/AlphaTabs/components/List/index.ts delete mode 100644 polaris-react/src/components/AlphaTabs/components/List/tests/List.test.tsx delete mode 100644 polaris-react/src/components/AlphaTabs/components/Panel/Panel.tsx delete mode 100644 polaris-react/src/components/AlphaTabs/components/Panel/index.ts delete mode 100644 polaris-react/src/components/AlphaTabs/components/Panel/tests/Panel.test.tsx delete mode 100644 polaris-react/src/components/AlphaTabs/components/Tab/Tab.tsx delete mode 100644 polaris-react/src/components/AlphaTabs/components/Tab/index.ts delete mode 100644 polaris-react/src/components/AlphaTabs/components/Tab/tests/Tab.test.tsx delete mode 100644 polaris-react/src/components/AlphaTabs/components/TabMeasurer/TabMeasurer.tsx delete mode 100644 polaris-react/src/components/AlphaTabs/components/TabMeasurer/index.ts delete mode 100644 polaris-react/src/components/AlphaTabs/components/TabMeasurer/tests/TabMeasurer.test.tsx delete mode 100644 polaris-react/src/components/AlphaTabs/components/index.ts delete mode 100644 polaris-react/src/components/AlphaTabs/index.ts delete mode 100644 polaris-react/src/components/AlphaTabs/tests/AlphaTabs.test.tsx delete mode 100644 polaris-react/src/components/AlphaTabs/types.ts delete mode 100644 polaris-react/src/components/AlphaTabs/utilities.ts delete mode 100644 polaris-react/src/components/Caption/Caption.scss delete mode 100644 polaris-react/src/components/Caption/Caption.stories.tsx delete mode 100644 polaris-react/src/components/Caption/Caption.tsx delete mode 100644 polaris-react/src/components/Caption/index.ts delete mode 100644 polaris-react/src/components/Caption/tests/Caption.test.tsx delete mode 100644 polaris-react/src/components/Card/Card.scss delete mode 100644 polaris-react/src/components/Card/components/Header/Header.tsx delete mode 100644 polaris-react/src/components/Card/components/Header/index.ts delete mode 100644 polaris-react/src/components/Card/components/Header/tests/Header.test.tsx delete mode 100644 polaris-react/src/components/Card/components/Section/Section.tsx delete mode 100644 polaris-react/src/components/Card/components/Section/index.ts delete mode 100644 polaris-react/src/components/Card/components/Section/tests/Section.test.tsx delete mode 100644 polaris-react/src/components/Card/components/Subsection/Subsection.tsx delete mode 100644 polaris-react/src/components/Card/components/Subsection/index.ts delete mode 100644 polaris-react/src/components/Card/components/Subsection/tests/Subsection.test.tsx delete mode 100644 polaris-react/src/components/Card/components/index.ts delete mode 100644 polaris-react/src/components/DisplayText/DisplayText.scss delete mode 100644 polaris-react/src/components/DisplayText/DisplayText.stories.tsx delete mode 100644 polaris-react/src/components/DisplayText/DisplayText.tsx delete mode 100644 polaris-react/src/components/DisplayText/index.ts delete mode 100644 polaris-react/src/components/DisplayText/tests/DisplayText.test.tsx delete mode 100644 polaris-react/src/components/Filters/components/ConnectedFilterControl/ConnectedFilterControl.scss delete mode 100644 polaris-react/src/components/Filters/components/ConnectedFilterControl/ConnectedFilterControl.tsx delete mode 100644 polaris-react/src/components/Filters/components/ConnectedFilterControl/components/Item/Item.tsx delete mode 100644 polaris-react/src/components/Filters/components/ConnectedFilterControl/components/Item/index.ts delete mode 100644 polaris-react/src/components/Filters/components/ConnectedFilterControl/components/Item/tests/Item.test.tsx delete mode 100644 polaris-react/src/components/Filters/components/ConnectedFilterControl/components/index.ts delete mode 100644 polaris-react/src/components/Filters/components/ConnectedFilterControl/index.ts delete mode 100644 polaris-react/src/components/Filters/components/ConnectedFilterControl/tests/ConnectedFilterControl.test.tsx rename polaris-react/src/components/{AlphaFilters => Filters}/components/FilterPill/FilterPill.scss (84%) rename polaris-react/src/components/{AlphaFilters => Filters}/components/FilterPill/FilterPill.tsx (100%) rename polaris-react/src/components/{AlphaFilters => Filters}/components/FilterPill/index.ts (100%) rename polaris-react/src/components/{AlphaFilters => Filters}/components/FilterPill/tests/FilterPill.test.tsx (100%) rename polaris-react/src/components/{AlphaFilters => Filters}/components/SearchField/SearchField.tsx (100%) rename polaris-react/src/components/{AlphaFilters => Filters}/components/SearchField/index.ts (100%) rename polaris-react/src/components/{AlphaFilters => Filters}/components/SearchField/tests/SearchField.test.tsx (100%) delete mode 100644 polaris-react/src/components/Filters/components/TagsWrapper/TagsWrapper.tsx delete mode 100644 polaris-react/src/components/Filters/components/TagsWrapper/index.ts delete mode 100644 polaris-react/src/components/Filters/components/TagsWrapper/tests/TagsWrapper.test.tsx delete mode 100644 polaris-react/src/components/Heading/Heading.scss delete mode 100644 polaris-react/src/components/Heading/Heading.stories.tsx delete mode 100644 polaris-react/src/components/Heading/Heading.tsx delete mode 100644 polaris-react/src/components/Heading/index.ts delete mode 100644 polaris-react/src/components/Heading/tests/Heading.test.tsx delete mode 100644 polaris-react/src/components/KonamiCode/KonamiCode.tsx delete mode 100644 polaris-react/src/components/KonamiCode/index.ts delete mode 100644 polaris-react/src/components/KonamiCode/tests/KonamiCode.test.tsx delete mode 100644 polaris-react/src/components/Stack/Stack.scss delete mode 100644 polaris-react/src/components/Stack/Stack.stories.tsx delete mode 100644 polaris-react/src/components/Stack/Stack.tsx delete mode 100644 polaris-react/src/components/Stack/components/Item/Item.tsx delete mode 100644 polaris-react/src/components/Stack/components/Item/index.ts delete mode 100644 polaris-react/src/components/Stack/components/index.ts delete mode 100644 polaris-react/src/components/Stack/index.ts delete mode 100644 polaris-react/src/components/Stack/tests/Stack.test.tsx delete mode 100644 polaris-react/src/components/Subheading/Subheading.scss delete mode 100644 polaris-react/src/components/Subheading/Subheading.stories.tsx delete mode 100644 polaris-react/src/components/Subheading/Subheading.tsx delete mode 100644 polaris-react/src/components/Subheading/index.ts delete mode 100644 polaris-react/src/components/Subheading/tests/Subheading.test.tsx rename polaris-react/src/components/{AlphaTabs => Tabs}/components/CreateViewModal/CreateViewModal.tsx (100%) rename polaris-react/src/components/{AlphaTabs => Tabs}/components/CreateViewModal/index.ts (100%) rename polaris-react/src/components/{AlphaTabs => Tabs}/components/CreateViewModal/tests/CreateViewModal.test.tsx (100%) rename polaris-react/src/components/{AlphaTabs => Tabs}/components/Tab/components/DuplicateModal/DuplicateModal.tsx (100%) rename polaris-react/src/components/{AlphaTabs => Tabs}/components/Tab/components/DuplicateModal/index.ts (100%) rename polaris-react/src/components/{AlphaTabs => Tabs}/components/Tab/components/DuplicateModal/tests/DuplicateModal.test.tsx (100%) rename polaris-react/src/components/{AlphaTabs => Tabs}/components/Tab/components/RenameModal/RenameModal.tsx (100%) rename polaris-react/src/components/{AlphaTabs => Tabs}/components/Tab/components/RenameModal/index.ts (100%) rename polaris-react/src/components/{AlphaTabs => Tabs}/components/Tab/components/RenameModal/tests/RenameModal.test.tsx (100%) rename polaris-react/src/components/{AlphaTabs => Tabs}/components/Tab/components/index.ts (100%) delete mode 100644 polaris-react/src/components/TextStyle/TextStyle.scss delete mode 100644 polaris-react/src/components/TextStyle/TextStyle.stories.tsx delete mode 100644 polaris-react/src/components/TextStyle/TextStyle.tsx delete mode 100644 polaris-react/src/components/TextStyle/index.ts delete mode 100644 polaris-react/src/components/TextStyle/tests/TextStyle.test.tsx delete mode 100644 polaris-react/src/components/VisuallyHidden/VisuallyHidden.scss delete mode 100644 polaris-react/src/components/VisuallyHidden/VisuallyHidden.stories.tsx delete mode 100644 polaris-react/src/components/VisuallyHidden/VisuallyHidden.tsx delete mode 100644 polaris-react/src/components/VisuallyHidden/index.ts delete mode 100644 polaris-react/src/utilities/unique-id/context.ts delete mode 100644 polaris-react/src/utilities/unique-id/hooks.ts delete mode 100644 polaris-react/src/utilities/unique-id/index.ts delete mode 100644 polaris-react/src/utilities/unique-id/tests/hooks.test.tsx delete mode 100644 polaris-react/src/utilities/unique-id/tests/unique-id-factory.test.ts delete mode 100644 polaris-react/src/utilities/unique-id/unique-id-factory.ts delete mode 100644 polaris-tokens/src/token-groups/depth.ts delete mode 100644 polaris-tokens/src/token-groups/legacy.ts delete mode 100644 polaris-tokens/src/token-groups/shape.ts delete mode 100644 polaris-tokens/src/token-groups/spacing.ts delete mode 100644 polaris.shopify.com/content/components/deprecated/card.md delete mode 100644 polaris.shopify.com/content/components/deprecated/stack.md rename polaris.shopify.com/content/components/layout-and-structure/{alpha-card.md => card.md} (88%) delete mode 100644 polaris.shopify.com/content/components/navigation/alpha-tabs.md delete mode 100644 polaris.shopify.com/content/components/selection-and-input/alpha-filters.md create mode 100644 polaris.shopify.com/content/whats-new/version-11-color.md create mode 100644 polaris.shopify.com/content/whats-new/version-11-layout.md delete mode 100644 polaris.shopify.com/pages/examples/alpha-filters-disabled.tsx delete mode 100644 polaris.shopify.com/pages/examples/alpha-filters-with-a-data-table.tsx delete mode 100644 polaris.shopify.com/pages/examples/alpha-filters-with-a-resource-list.tsx delete mode 100644 polaris.shopify.com/pages/examples/alpha-filters-with-children-content.tsx delete mode 100644 polaris.shopify.com/pages/examples/alpha-filters-with-query-field-hidden.tsx delete mode 100644 polaris.shopify.com/pages/examples/alpha-tabs-default.tsx delete mode 100644 polaris.shopify.com/pages/examples/alpha-tabs-fitted.tsx delete mode 100644 polaris.shopify.com/pages/examples/alpha-tabs-inside-of-a-card.tsx delete mode 100644 polaris.shopify.com/pages/examples/alpha-tabs-with-actions.tsx delete mode 100644 polaris.shopify.com/pages/examples/alpha-tabs-with-custom-disclosure.tsx rename polaris.shopify.com/pages/examples/{alpha-card-default.tsx => card-default.tsx} (55%) rename polaris.shopify.com/pages/examples/{alpha-card-with-rounded-corners.tsx => card-with-rounded-corners.tsx} (53%) rename polaris.shopify.com/pages/examples/{alpha-card-with-subdued-background.tsx => card-with-subdued-background.tsx} (52%) rename polaris.shopify.com/pages/examples/{alpha-card-with-varying-padding.tsx => card-with-varying-padding.tsx} (70%) delete mode 100644 polaris.shopify.com/pages/examples/deprecated-card-with-a-subdued-section.tsx delete mode 100644 polaris.shopify.com/pages/examples/deprecated-card-with-all-elements.tsx delete mode 100644 polaris.shopify.com/pages/examples/deprecated-card-with-custom-footer-actions.tsx delete mode 100644 polaris.shopify.com/pages/examples/deprecated-card-with-custom-react-node-title.tsx delete mode 100644 polaris.shopify.com/pages/examples/deprecated-card-with-destructive-action.tsx delete mode 100644 polaris.shopify.com/pages/examples/deprecated-card-with-destructive-footer-action.tsx delete mode 100644 polaris.shopify.com/pages/examples/deprecated-card-with-flushed-sections.tsx delete mode 100644 polaris.shopify.com/pages/examples/deprecated-card-with-footer-actions.tsx delete mode 100644 polaris.shopify.com/pages/examples/deprecated-card-with-header-actions.tsx delete mode 100644 polaris.shopify.com/pages/examples/deprecated-card-with-multiple-footer-actions.tsx delete mode 100644 polaris.shopify.com/pages/examples/deprecated-card-with-multiple-sections.tsx delete mode 100644 polaris.shopify.com/pages/examples/deprecated-card-with-multiple-titled-sections.tsx delete mode 100644 polaris.shopify.com/pages/examples/deprecated-card-with-sections-and-actions.tsx delete mode 100644 polaris.shopify.com/pages/examples/deprecated-card-with-separate-header.tsx delete mode 100644 polaris.shopify.com/pages/examples/deprecated-card-with-subdued-for-secondary-content.tsx delete mode 100644 polaris.shopify.com/pages/examples/deprecated-card-with-subsection.tsx rename polaris.shopify.com/pages/examples/{alpha-filters-with-query-field-disabled.tsx => filters-with-query-field-disabled.tsx} (95%) rename polaris.shopify.com/pages/examples/{alpha-filters-with-some-disabled.tsx => filters-with-some-disabled.tsx} (87%) delete mode 100644 polaris.shopify.com/pages/examples/stack-default.tsx delete mode 100644 polaris.shopify.com/pages/examples/stack-fill-available-space-proportionally.tsx delete mode 100644 polaris.shopify.com/pages/examples/stack-non-wrapping.tsx delete mode 100644 polaris.shopify.com/pages/examples/stack-spacing.tsx delete mode 100644 polaris.shopify.com/pages/examples/stack-vertical-centering.tsx delete mode 100644 polaris.shopify.com/pages/examples/stack-where-a-single-item-fills-the-remaining-space.tsx delete mode 100644 polaris.shopify.com/pages/examples/stack-where-items-fill-space-evenly.tsx rename polaris.shopify.com/pages/examples/{alpha-tabs-with-badge-content.tsx => tabs-inside-of-a-card.tsx} (50%) create mode 100644 polaris.shopify.com/pages/examples/tabs-with-actions.tsx delete mode 100644 polaris.shopify.com/public/images/components/deprecated/stack.png delete mode 100644 polaris.shopify.com/public/images/components/layout-and-structure/alpha-card.png rename polaris.shopify.com/public/images/components/{deprecated => layout-and-structure}/card.png (100%) delete mode 100644 polaris.shopify.com/public/images/components/navigation/alpha-tabs.png delete mode 100644 polaris.shopify.com/public/images/components/selection-and-input/alpha-filters.png create mode 100644 polaris.shopify.com/public/images/updates/alias-tokens.png create mode 100644 polaris.shopify.com/public/images/updates/new-colors-cover.png create mode 100644 polaris.shopify.com/public/images/updates/new-palette.png create mode 100644 polaris.shopify.com/public/images/updates/old-palette.png diff --git a/.changeset/afraid-scissors-work.md b/.changeset/afraid-scissors-work.md new file mode 100644 index 00000000000..d3fe624cf50 --- /dev/null +++ b/.changeset/afraid-scissors-work.md @@ -0,0 +1,6 @@ +--- +'@shopify/polaris': major +--- + +Changed `breadcrumbs` from an array to a single `backAction` since only one is supported. +Removed deprecated `breadcrumbs` prop from `Page` and `Breadcrumbs`. diff --git a/.changeset/cool-starfishes-cry.md b/.changeset/cool-starfishes-cry.md new file mode 100644 index 00000000000..126786d4c15 --- /dev/null +++ b/.changeset/cool-starfishes-cry.md @@ -0,0 +1,7 @@ +--- +'@shopify/polaris-cli': major +'@shopify/polaris': major +'polaris.shopify.com': major +--- + +Removed support for multiple versions of TypeScript with `downlevel-dts` diff --git a/.changeset/few-forks-change.md b/.changeset/few-forks-change.md new file mode 100644 index 00000000000..ed4db1d95aa --- /dev/null +++ b/.changeset/few-forks-change.md @@ -0,0 +1,5 @@ +--- +'@shopify/polaris': major +--- + +No longer transpile optional chaining, nullish coalescing or numeric separators, as our target browser environments all have native support for these syntaxes. This removes support for apps using webpack4, which unable to parse these syntaxes. diff --git a/.changeset/four-tables-hide.md b/.changeset/four-tables-hide.md new file mode 100644 index 00000000000..a1e0a7149f4 --- /dev/null +++ b/.changeset/four-tables-hide.md @@ -0,0 +1,5 @@ +--- +'@shopify/polaris': major +--- + +Removed support for React version 16 and 17 in favor of version 18 as the minimum supported version diff --git a/.changeset/great-cameras-matter.md b/.changeset/great-cameras-matter.md new file mode 100644 index 00000000000..67b759a69d5 --- /dev/null +++ b/.changeset/great-cameras-matter.md @@ -0,0 +1,6 @@ +--- +'@shopify/polaris': major +'polaris.shopify.com': minor +--- + +Replaced `Tabs` with `AlphaTabs` and replaced `Filters` with `AlphaFilters` component code diff --git a/.changeset/light-mails-type.md b/.changeset/light-mails-type.md new file mode 100644 index 00000000000..db6ef0bbda6 --- /dev/null +++ b/.changeset/light-mails-type.md @@ -0,0 +1,6 @@ +--- +'@shopify/polaris': major +'polaris.shopify.com': minor +--- + +Replaced `Stack` with `AlphaStack` diff --git a/.changeset/light-steaks-promise.md b/.changeset/light-steaks-promise.md new file mode 100644 index 00000000000..852d76d4ca9 --- /dev/null +++ b/.changeset/light-steaks-promise.md @@ -0,0 +1,5 @@ +--- +'@shopify/polaris': major +--- + +Removed `Polaris.VERSION` from the global window object diff --git a/.changeset/metal-spies-serve.md b/.changeset/metal-spies-serve.md new file mode 100644 index 00000000000..296b0a42dcc --- /dev/null +++ b/.changeset/metal-spies-serve.md @@ -0,0 +1,5 @@ +--- +'@shopify/polaris': major +--- + +Removed deprecated `breadcrumbs` prop from `SkeletonPage` diff --git a/.changeset/nasty-suns-hang.md b/.changeset/nasty-suns-hang.md new file mode 100644 index 00000000000..75c65a21877 --- /dev/null +++ b/.changeset/nasty-suns-hang.md @@ -0,0 +1,5 @@ +--- +'polaris.shopify.com': patch +--- + +Updated `Text` component status from beta to stable diff --git a/.changeset/quiet-toes-play.md b/.changeset/quiet-toes-play.md new file mode 100644 index 00000000000..30d34e6ff32 --- /dev/null +++ b/.changeset/quiet-toes-play.md @@ -0,0 +1,12 @@ +--- +'@shopify/polaris-cli': major +'polaris-for-vscode': major +'@shopify/polaris-icons': major +'@shopify/polaris-migrator': major +'@shopify/polaris': major +'@shopify/polaris-tokens': major +'polaris.shopify.com': major +'@shopify/stylelint-polaris': major +--- + +Removed support for NodeJS version 14 and set version 16 as minimum supported version diff --git a/.changeset/real-lemons-sniff.md b/.changeset/real-lemons-sniff.md new file mode 100644 index 00000000000..d4c1690f2a0 --- /dev/null +++ b/.changeset/real-lemons-sniff.md @@ -0,0 +1,6 @@ +--- +'@shopify/polaris': major +'polaris.shopify.com': minor +--- + +Removed deprecated `DisplayText`, `Heading`, `Subheading`, `Caption`, `TextStyle`, and `VisuallyHidden` components diff --git a/.changeset/rude-teachers-doubt.md b/.changeset/rude-teachers-doubt.md new file mode 100644 index 00000000000..e309be75bfa --- /dev/null +++ b/.changeset/rude-teachers-doubt.md @@ -0,0 +1,5 @@ +--- +'polaris.shopify.com': minor +--- + +Added "What's new" color page and updated design/colors for the new color tokens diff --git a/.changeset/six-jokes-drum.md b/.changeset/six-jokes-drum.md new file mode 100644 index 00000000000..44ceb0da8fb --- /dev/null +++ b/.changeset/six-jokes-drum.md @@ -0,0 +1,6 @@ +--- +'@shopify/polaris': major +'polaris.shopify.com': minor +--- + +Replaced `Card` with `AlphaCard` diff --git a/.changeset/slimy-countries-learn.md b/.changeset/slimy-countries-learn.md new file mode 100644 index 00000000000..78b2f0e21ed --- /dev/null +++ b/.changeset/slimy-countries-learn.md @@ -0,0 +1,5 @@ +--- +'@shopify/polaris': major +--- + +Removed `KonamiCode` component diff --git a/.changeset/stupid-vans-kick.md b/.changeset/stupid-vans-kick.md new file mode 100644 index 00000000000..07bbc13081a --- /dev/null +++ b/.changeset/stupid-vans-kick.md @@ -0,0 +1,5 @@ +--- +'@shopify/polaris': major +--- + +Removed deprecated `preventMeasuringOnChildUpdate` prop on `Collapsible` diff --git a/.eslintrc.js b/.eslintrc.js index 94404dcb12d..d0f6a97af99 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -150,12 +150,6 @@ module.exports = { 'import/extensions': 'off', 'import/no-default-export': 'off', 'import/no-anonymous-default-export': 'off', - // We could omit this if we set `engines` fields properly - // As we don't set them then eslint thinks we're using node 8 - 'node/no-unsupported-features/node-builtins': [ - 'error', - {version: '>=16.0.0'}, - ], }, }, { diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index 33b17868110..00000000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,18 +0,0 @@ -# Temporary file to alert team members on the layout foundations project of changes to Card and Stack. - -/polaris-react/src/components/AlphaCard/ @samrose3 -/polaris-react/src/components/Card/ @laurkim @aveline -/polaris-react/src/components/LegacyCard/ @laurkim @aveline -/polaris-react/src/components/Stack/ @laurkim @aveline -/polaris-react/src/components/LegacyStack/ @laurkim @aveline -/polaris-react/src/components/AlphaFilters/ @samrose3 -/polaris-react/src/components/Filters/ @samrose3 -/polaris-react/src/components/AlphaTabs/ @samrose3 -/polaris-react/src/components/Tabs/ @samrose3 - -/polaris.shopify.com/content/components/card.md @laurkim @aveline -/polaris.shopify.com/content/components/legacy-card.md @laurkim @aveline -/polaris.shopify.com/content/components/stack.md @laurkim @aveline -/polaris.shopify.com/content/components/legacy-stack.md @laurkim @aveline - - diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 1f564d2fd4e..c180ab5b00f 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -137,7 +137,7 @@ Each contributor is required to [sign a CLA](https://cla.shopify.com/). This pro ### Contribution prerequisites -- You have Node installed at v16.13.0+ and Yarn at v1.22.18+ +- You have Node installed at v18.12.0+ and Yarn at v1.22.18+ ### Best practices diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index cffce71434f..3493dbfeb97 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -21,10 +21,10 @@ jobs: with: fetch-depth: 0 - - name: Setup Node with v16.13.0 + - name: Setup Node with v18.12.0 uses: actions/setup-node@v3 with: - node-version: 16.13.0 + node-version: 18.12.0 - name: Check for Changeset run: npx @changesets/cli status --since="origin/main" diff --git a/.github/workflows/ci-a11y-vrt.yml b/.github/workflows/ci-a11y-vrt.yml index 36252c35f2b..1b0b3018ab7 100644 --- a/.github/workflows/ci-a11y-vrt.yml +++ b/.github/workflows/ci-a11y-vrt.yml @@ -32,10 +32,10 @@ jobs: sudo rm -rf "/usr/local/share/boost" sudo rm -rf "$AGENT_TOOLSDIRECTORY" - - name: Setup Node with v16.13.0 + - name: Setup Node with v18.12.0 uses: actions/setup-node@v3 with: - node-version: 16.13.0 + node-version: 18.12.0 cache: yarn - name: Restore cache @@ -72,10 +72,10 @@ jobs: with: fetch-depth: 0 # Chromatic git history to track changes - - name: Setup Node with v16.13.0 + - name: Setup Node with v18.12.0 uses: actions/setup-node@v3 with: - node-version: 16.13.0 + node-version: 18.12.0 cache: yarn - name: Restore cache diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f4b44aae00..184b77b80f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: ['14.17.0', '16.13.0'] + node-version: ['16.17.0', '18.12.0'] steps: - name: Checkout branch uses: actions/checkout@v3 diff --git a/.github/workflows/size-limit.yml b/.github/workflows/size-limit.yml deleted file mode 100644 index b1c407d2305..00000000000 --- a/.github/workflows/size-limit.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Size limit - -on: - pull_request: - paths: - - 'polaris-react/**' - -jobs: - size: - name: Size limit - runs-on: ubuntu-latest - env: - CI_JOB_NUMBER: 1 - steps: - - uses: actions/checkout@v3 - - - name: Free up space on GitHub image - run: | - # Based on the official advice: - # https://github.com/actions/virtual-environments/issues/2840#issuecomment-790492173 - sudo rm -rf /usr/share/dotnet - sudo rm -rf /opt/ghc - sudo rm -rf "/usr/local/share/boost" - sudo rm -rf "$AGENT_TOOLSDIRECTORY" - - - uses: actions/setup-node@v3 - with: - node-version: 16 - cache: yarn - - - name: Restore cache - uses: actions/cache@v3 - with: - path: | - **/.eslintcache - **/.turbo - node_modules/.cache/turbo - key: ${{ runner.os }}-test-v1-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-test-v1- - - - uses: andresz1/size-limit-action@v1.7.0 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - build_script: yarn build --filter=@shopify/polaris diff --git a/.gitignore b/.gitignore index 216460d8af7..5b6ebc0a66d 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ Session.vim /polaris.shopify.com/public/sitemap.xml /polaris.shopify.com/public/og-images /polaris.shopify.com/public/playroom +storybook-static diff --git a/.nvmrc b/.nvmrc index ff650592a1e..9dfef472196 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v16.13.0 +v18.12.0 diff --git a/babel.config.js b/babel.config.js index 0603bcc6c33..6bfdf254b86 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,16 +1,58 @@ /** * @type {import('@babel/core').TransformOptions} */ -module.exports = { - presets: [['@shopify/babel-preset', {typescript: true, react: true}]], - babelrcRoots: [ - '.', - // Note: The following projects use rootMode: 'upward' to inherit - // and merge with this root level config. - './polaris-codemods', - './polaris-migrator', - './polaris-tokens', - './polaris-icons', - './polaris-react', - ], +module.exports = function (api) { + const envName = api.env(); + const development = envName === 'development' || envName === 'test'; + return { + presets: [ + [ + '@babel/preset-env', + {useBuiltIns: 'entry', corejs: '3.0', bugfixes: true}, + ], + ['@babel/preset-typescript'], + ['@babel/preset-react', {development, useBuiltIns: true}], + ], + assumptions: { + setPublicClassFields: true, + privateFieldsAsProperties: true, + // nothing accesses `document.all`: + noDocumentAll: true, + // nothing relies on class constructors invoked without `new` throwing: + noClassCalls: true, + // nothing should be relying on tagged template strings being frozen: + mutableTemplateObject: true, + // nothing is relying on Function.prototype.length: + ignoreFunctionLength: true, + // nothing is relying on mutable re-exported bindings: + constantReexports: true, + // don't bother marking Module records non-enumerable: + enumerableModuleMeta: true, + // nothing uses [[Symbol.toPrimitive]]: + // (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toPrimitive) + ignoreToPrimitiveHint: true, + // nothing relies on spread copying Symbol keys: ({...{ [Symbol()]: 1 }}) + objectRestNoSymbols: true, + // nothing relies on `new (() => {})` throwing: + noNewArrows: true, + // transpile object spread to assignment instead of defineProperty(): + setSpreadProperties: true, + // nothing should be using custom iterator protocol: + skipForOfIteratorClosing: true, + // nothing inherits from a constructor function with explicit return value: + superIsCallableConstructor: true, + // nothing relies on CJS-transpiled namespace imports having all properties prior to module execution completing: + noIncompleteNsImportDetection: true, + }, + babelrcRoots: [ + '.', + // Note: The following projects use rootMode: 'upward' to inherit + // and merge with this root level config. + './polaris-codemods', + './polaris-migrator', + './polaris-tokens', + './polaris-icons', + './polaris-react', + ], + }; }; diff --git a/dev.yml b/dev.yml index 9ff7e7c0a44..83940cd41d2 100644 --- a/dev.yml +++ b/dev.yml @@ -2,7 +2,7 @@ name: polaris up: - node: yarn: v1.22.18 - version: v16.13.0 # to be kept in sync with .nvmrc and .github/workflows/ci.yml + version: v18.12.0 # to be kept in sync with .nvmrc and .github/workflows/ci.yml - custom: name: Build monorepo met?: yarn build --output-logs=none diff --git a/documentation/Nodejs support.md b/documentation/Nodejs support.md index 3ad0a11818a..dfe4e9dc591 100644 --- a/documentation/Nodejs support.md +++ b/documentation/Nodejs support.md @@ -16,26 +16,26 @@ The `package.json` engines. This should match the `.github/workflows/ci.yml` and ```json "engines": { - "node": "^14.17.0 || ^16.13.0" + "node": "^16.17.0 || >=18.12.0" }, ``` The GitHub actions `.github/workflows/ci.yml` file. This should match the `package.json` and list all supported versions. ```yml -node-version: ['14.17.0', '16.13.0'] +node-version: ['16.17.0', '18.12.0'] ``` The `dev.yml` file which creates a local development environment. This should match the `.nvmrc` file. ```yml -version: v16.13.0 +version: v18.12.0 ``` The `.nvmrc` file for local development. This should match the `dev.yml` file. ``` -v16.13.0 +v18.12.0 ``` The `shipit.yml` files. This should point towards the GitHub Actions the packages require to pass before publishing. @@ -43,11 +43,13 @@ The `shipit.yml` files. This should point towards the GitHub Actions the package ```yml ci: require: - - 'Validate with Node v14.17.0' - - 'Validate with Node v16.13.0' + - 'Validate with Node v16.17.0' + - 'Validate with Node v18.12.0' merge: require: - - 'Validate with Node v14.17.0' - - 'Validate with Node v16.13.0' + - 'Validate with Node v16.17.0' + - 'Validate with Node v18.12.0' ``` + +The `rollup.config.mjs` for some monorepo packages. This should match the minimum supported version. diff --git a/documentation/guides/migrating-from-v10-to-v11.md b/documentation/guides/migrating-from-v10-to-v11.md new file mode 100644 index 00000000000..570c3a11343 --- /dev/null +++ b/documentation/guides/migrating-from-v10-to-v11.md @@ -0,0 +1,1237 @@ +# Migrating from v10 to v11 + +Polaris v11.0.0 ([full release notes](https://github.com/Shopify/polaris/releases/tag/v11.0.0)) features changes to supported versions for dependencies, removal of components, renamed components, and token changes. + +## Table of Contents + +- [Node support](#node-support) +- [React support](#react-support) +- [Webpack support](#webpack-support) +- [TypeScript](#typescript) +- [Components](#components) + - [Removed `Collapsible` deprecated `preventMeasuringOnChildrenUpdate` prop](#removed-collapsible-deprecated-preventmeasuringonchildrenupdate-prop) + - [Removed `SkeletonPage` deprecated `breadcrumbs` prop](#removed-skeletonpage-deprecated-breadcrumbs-prop) + - [Removed `Page` deprecated `breadcrumbs` prop](#removed-page-deprecated-breadcrumbs-prop) + - [Removed `Breadcrumbs` deprecated `breadcrumbs` prop](#removed-breadcrumbs-deprecated-breadcrumbs-prop) + - [Removed `KonamiCode`](#removed-konamicode) + - [Removed `DisplayText`](#removed-displaytext) + - [Removed `Heading`](#removed-heading) + - [Removed `Subheading`](#removed-subheading) + - [Removed `Caption`](#removed-caption) + - [Removed `TextStyle`](#removed-textstyle) + - [Removed `VisuallyHidden`](#removed-visuallyhidden) + - [Migrated `Stack` to `LegacyStack`](#migrated-stack-to-legacystack) + - [Migrated `Card` to `LegacyCard`](#migrated-card-to-legacycard) + - [Migrated `Filters` to `LegacyFilters`](#migrated-filters-to-legacyfilters) + - [Migrated `Tabs` to `LegacyTabs`](#migrated-tabs-to-legacytabs) + - [Renamed `Inline`](#renamed-inline) + - [Renamed `AlphaStack`](#renamed-alphastack) + - [Renamed `AlphaCard`](#renamed-alphacard) + - [Renamed `AlphaFilters`](#renamed-alphafilters) + - [Renamed `AlphaTabs`](#renamed-alphatabs) + - [Recommended component migration workflow](#recommended-component-migration-workflow) +- [Tokens](#tokens) + - [Border](#border) + - [Color](#color) + - [Depth](#depth) + - [Motion](#motion) + - [Legacy](#legacy) + - [Z-index](#z-index) + - [Recommended token migration workflow](#recommended-token-migration-workflow) + +## Node support + +NodeJS version 14 is no longer supported. NodeJS 18 is recommended and 16 is the minimum supported version. + +## React support + +React version 16 and 17 is no longer supported. React 18 is the minimum supported version. + +## Webpack support + +Webpack version 4 is no longer supported. Webpack 5 is the minimum supported version. + +## TypeScript + +Built types in `@shopify/polaris` have moved from `build/ts/latest` to `build/ts`. + +### Legacy TypeScript Support + +Polaris no longer supports multiple versions of TypeScript with `downlevel-dts`. Polaris only builds one set of types based on the current version of TypeScript in the project. + +## Components + +The following components have either been renamed, migrated, or removed. Please review each component section to determine whether you can resolve these breaking changes with a migration or if they need to be updated manually. + +### Removed `Collapsible` deprecated `preventMeasuringOnChildrenUpdate` prop + +The deprecated `preventMeasuringOnChildrenUpdate` prop has been removed from the `Collapsible` component and is no longer supported. + +### Removed `SkeletonPage` deprecated `breadcrumbs` prop + +The deprecated `breadcrumbs` prop has been removed from the `SkeletonPage` component and is no longer supported. The new `backAction` prop serves the same functionality and accepts a boolean. + +#### Migration + +To replace the `breadcrumbs` prop with `backAction`, you can run the generic [react-rename-component-prop](https://polaris.shopify.com/tools/polaris-migrator#generic-migrations) migration. Please reference the [recommended component migration workflow](#recommended-component-migration-workflow) section below for additional migration support. + +```diff +- ++ +``` + +```sh +npx @shopify/polaris-migrator react-rename-component-prop --componentName="SkeletonPage" --from="breadcrumbs" --to="backAction" +``` + +#### Post-migration validation + +After migrating you can use the following RegExp to check for any additional instances of `` across all file types: + +```regex +\w](?:[^>]|\n)*?breadcrumbs +``` + +### Removed `Page` deprecated `breadcrumbs` prop + +The deprecated `breadcrumbs` prop has been removed from the `Page` component and is no longer supported. The new `backAction` prop serves the same functionality and accepts a [`LinkAction` object](https://github.com/Shopify/polaris/blob/main/polaris-react/src/types.ts#L113-L122). + +#### Migration + +To replace the `breadcrumbs` prop with `backAction`, you can run the [v11-react-update-page-breadcrumbs](https://polaris.shopify.com/tools/polaris-migrator#v11-react-update-page-breadcrumbs) migration. Please reference the [recommended component migration workflow](#recommended-component-migration-workflow) section below for additional migration support. + +```diff +- ++ +``` + +```sh +npx @shopify/polaris-migrator v11-react-update-page-breadcrumbs +``` + +#### Post-migration validation + +After migrating you can use the following RegExp to check for any additional instances of `` across all file types: + +``` +\w](?:[^>]|\n)*?breadcrumbs +``` + +### Removed `Breadcrumbs` deprecated `breadcrumbs` prop + +The deprecated `breadcrumbs` prop has been removed from the `Breadcrumbs` component and is no longer supported. The new `backAction` prop serves the same functionality and accepts a [`LinkAction` object](https://github.com/Shopify/polaris/blob/main/polaris-react/src/types.ts#L119-L128). + +#### Migration + +To replace the `breadcrumbs` prop with `backAction`, you can run the generic [react-rename-component-prop](https://polaris.shopify.com/tools/polaris-migrator#generic-migrations) migration. Please reference the [recommended component migration workflow](#recommended-component-migration-workflow) section below for additional migration support. + +```diff +- ++ +``` + +```sh +npx @shopify/polaris-migrator react-rename-component-prop --componentName="Breadcrumbs" --from="breadcrumbs" --to="backAction" +``` + +#### Post-migration validation + +After migrating you can use the following RegExp to check for any additional instances of `` across all file types: + +``` +\w](?:[^>]|\n)*?breadcrumbs +``` + +### Removed `KonamiCode` + +Low usage components are being removed from Polaris. We love fun but we also want to ensure we are shipping exactly what our users need. If you want to use this in your project, feel free to copy the [component sourcecode](https://github.com/Shopify/polaris/blob/%40shopify/polaris%4010.24.0/polaris-react/src/components/KonamiCode/KonamiCode.tsx). + +### Removed `DisplayText` + +The `DisplayText` component has been removed in favor of the `Text` component. The `Text` component simplifies designing with typography tokens and improves our foundation for flexibility and composability, all in one component. + +#### Migration + +To replace the six removed typography components (`DisplayText`, `Heading`, `Subheading`, `Caption`, `TextStyle`, and `VisuallyHidden`), you can run the [v10-react-replace-text-component](https://polaris.shopify.com/tools/polaris-migrator#v10-react-replace-text-components) migration. Please reference the [recommended component migration workflow](#recommended-component-migration-workflow) section below for additional migration support. + +```sh +npx @shopify/polaris-migrator v10-react-replace-text-components +``` + +> **Note**: To migrate only the `DisplayText` component, the migration can be supplemented with the `--componentName='DisplayText'` flag. + +```sh +npx @shopify/polaris-migrator v10-react-replace-text-components --componentName='DisplayText' +``` + +#### Post-migration validation + +After migrating, search for React instances or CSS class overrides of the `DisplayText` component across all file types in your project: + +```jsx +Sales this year ++ Sales this year +``` + +##### Medium + +```diff +- Sales this year ++ Sales this year +``` + +##### Large + +```diff +- Sales this year ++ Sales this year +``` + +##### Extra large + +```diff +- Sales this year ++ Sales this year +``` + +### Removed `Heading` + +The `Heading` component has been removed in favor of the `Text` component. The `Text` component simplifies designing with typography tokens and improves our foundation for flexibility and composability, all in one component. + +#### Migration + +To replace the six removed typography components (`DisplayText`, `Heading`, `Subheading`, `Caption`, `TextStyle`, and `VisuallyHidden`), you can run the [v10-react-replace-text-components](https://polaris.shopify.com/tools/polaris-migrator#v10-react-replace-text-components) migration. Please reference the [recommended component migration workflow](#recommended-component-migration-workflow) section below for additional migration support. + +```sh +npx @shopify/polaris-migrator v10-react-replace-text-components +``` + +> **Note**: To migrate only the `Heading` component, the migration can be supplemented with the `--componentName='Heading'` flag. + +```sh +npx @shopify/polaris-migrator v10-react-replace-text-components --componentName='Heading' +``` + +#### Post-migration validation + +After migrating, search for React instances or CSS class overrides of the `Heading` component across all file types in your project: + +```jsx +Online store dashboard ++ Online store dashboard +``` + +### Removed `Subheading` + +The `Subheading` component has been removed in favor of the `Text` component. The `Text` component simplifies designing with typography tokens and improves our foundation for flexibility and composability, all in one component. + +#### Migration + +To replace the six removed typography components (`DisplayText`, `Heading`, `Subheading`, `Caption`, `TextStyle`, and `VisuallyHidden`), you can run the [v10-react-replace-text-components](https://polaris.shopify.com/tools/polaris-migrator#v10-react-replace-text-components) migration. Please reference the [recommended component migration workflow](#recommended-component-migration-workflow) section below for additional migration support. + +```sh +npx @shopify/polaris-migrator v10-react-replace-text-components +``` + +> **Note**: To migrate only the `Subheading` component, the migration can be supplemented with the `--componentName='Subheading'` flag. + +```sh +npx @shopify/polaris-migrator v10-react-replace-text-components --componentName='Subheading' +``` + +#### Post-migration validation + +After migrating, search for React instances or CSS class overrides of the `Subheading` component across all file types in your project: + +```jsx +Accounts ++ Accounts +``` + +### Removed `Caption` + +The `Caption` component has been removed in favor of the `Text` component. The `Text` component simplifies designing with typography tokens and improves our foundation for flexibility and composability, all in one component. + +#### Migration + +To replace the six removed typography components (`DisplayText`, `Heading`, `Subheading`, `Caption`, `TextStyle`, and `VisuallyHidden`), you can run the [v10-react-replace-text-components](https://polaris.shopify.com/tools/polaris-migrator#v10-react-replace-text-components) migration. Please reference the [recommended component migration workflow](#recommended-component-migration-workflow) section below for additional migration support. + +```sh +npx @shopify/polaris-migrator v10-react-replace-text-components +``` + +> **Note**: To migrate only the `Caption` component, the migration can be supplemented with the `--componentName='Caption'` flag. + +```sh +npx @shopify/polaris-migrator v10-react-replace-text-components --componentName='Caption' +``` + +#### Post-migration validation + +After migrating, search for React instances or CSS class overrides of the `Caption` component across all file types in your project: + +```jsx +Received April 21, 2017 ++ Received April 21, 2017 +``` + +### Removed `TextStyle` + +The `TextStyle` component has been removed in favor of the `Text` component. The `Text` component simplifies designing with typography tokens and improves our foundation for flexibility and composability, all in one component. + +#### Migration + +To replace the six removed typography components (`DisplayText`, `Heading`, `Subheading`, `Caption`, `TextStyle`, and `VisuallyHidden`), you can run the [v10-react-replace-text-components](https://polaris.shopify.com/tools/polaris-migrator#v10-react-replace-text-components) migration. Please reference the [recommended component migration workflow](#recommended-component-migration-workflow) section below for additional migration support. + +```sh +npx @shopify/polaris-migrator v10-react-replace-text-components +``` + +> **Note**: To migrate only the `TextStyle` component, the migration can be supplemented with the `--componentName='TextStyle'` flag. + +```sh +npx @shopify/polaris-migrator v10-react-replace-text-components --componentName='TextStyle' +``` + +#### Post-migration validation + +After migrating, search for React instances or CSS class overrides of the `TextStyle` component across all file types in your project: + +```jsx +No supplier listed ++ No supplier listed +``` + +##### Strong + +```diff +- No supplier listed ++ No supplier listed +``` + +##### Positive + +```diff +- No supplier listed ++ No supplier listed +``` + +##### Negative + +```diff +- No supplier listed ++ No supplier listed +``` + +##### Warning + +```diff +- No supplier listed ++ No supplier listed +``` + +##### Code + +```diff +- No supplier listed ++ No supplier listed +``` + +### Removed `VisuallyHidden` + +The `VisuallyHidden` component has been removed in favor of the `Text` component. The `Text` component simplifies designing with typography tokens and improves our foundation for flexibility and composability, all in one component. + +#### Migration + +To replace the six removed typography components (`DisplayText`, `Heading`, `Subheading`, `Caption`, `TextStyle`, and `VisuallyHidden`), you can run the [v10-react-replace-text-components](https://polaris.shopify.com/tools/polaris-migrator#v10-react-replace-text-components) migration. Please reference the [recommended component migration workflow](#recommended-component-migration-workflow) section below for additional migration support. + +```sh +npx @shopify/polaris-migrator v10-react-replace-text-components +``` + +> **Note**: To migrate only the `VisuallyHidden` component, the migration can be supplemented with the `--componentName='VisuallyHidden'` flag. + +```sh +npx @shopify/polaris-migrator v10-react-replace-text-components --componentName='VisuallyHidden' +``` + +#### Post-migration validation + +After migrating, search for React instances or CSS class overrides of the `VisuallyHidden` component across all file types in your project: + +```jsx + +- Title and description +- ++ Title and description +``` + +### Migrated `Stack` to `LegacyStack` + +The `Stack` component was built prior to layout components such as `Box`, `HorizontalStack`, and `VerticalStack`. The layout components define the structure and spacing of user interfaces in a fast and composable way for consistent layouts across pages of an application. These components can be used to quickly create flexible pages and features without worrying about the underlying structure or CSS code. + +#### Migration + +The `LegacyStack` component is a duplicate of the existing `Stack` component. To replace `Stack` with `LegacyStack`, you can run the generic [react-rename-component](https://polaris.shopify.com/tools/polaris-migrator#generic-migrations) migration. Please reference the [recommended component migration workflow](#recommended-component-migration-workflow) section below for additional migration support. + +```diff +- ++ +- export interface StackProps {} ++ export interface LegacyStackProps {} +``` + +```sh +npx @shopify/polaris-migrator react-rename-component --renameFrom="Stack" --renameTo="LegacyStack" --renamePropsFrom="StackProps" --renamePropsTo="LegacyStackProps" +``` + +#### Post-migration validation + +After migrating, search for React instances or CSS class overrides of the `Stack` component across all file types in your project: + +```jsx + ++ +- export interface CardProps {} ++ export interface LegacyCardProps {} +``` + +```sh +npx @shopify/polaris-migrator react-rename-component --renameFrom="Card" --renameTo="LegacyCard" --renamePropsFrom="CardProps" --renamePropsTo="LegacyCardProps" +``` + +#### Post-migration validation + +After migrating, search for React instances or CSS class overrides of the `Card` component across all file types in your project: + +```jsx + ++ +- export interface FiltersProps {} ++ export interface LegacyFiltersProps {} +``` + +```sh +npx @shopify/polaris-migrator react-rename-component --renameFrom="Filters" --renameTo="LegacyFilters" --renamePropsFrom="FiltersProps" --renamePropsTo="LegacyFiltersProps" +``` + +#### Post-migration validation + +After migrating, search for React instances or CSS class overrides of the `Filters` component across all file types in your project: + +```jsx + ++ +- export interface TabsProps {} ++ export interface LegacyTabsProps {} +``` + +```sh +npx @shopify/polaris-migrator react-rename-component --renameFrom="Tabs" --renameTo="LegacyTabs" --renamePropsFrom="TabsProps" --renamePropsTo="LegacyTabsProps" +``` + +#### Post-migration validation + +After migrating, search for React instances or CSS class overrides of the `Tabs` component across all file types in your project: + +```jsx + ++ +- export interface InlineProps {} ++ export interface HorizontalStackProps {} +``` + +```sh +npx @shopify/polaris-migrator react-rename-component --renameFrom="Inline" --renameTo="HorizontalStack" --renamePropsFrom="InlineProps" --renamePropsTo="HorizontalStackProps" +``` + +#### Post-migration validation + +After migrating, search for React instances or CSS class overrides of the `Inline` component across all file types in your project: + +```jsx + ++ +- export interface AlphaStackProps {} ++ export interface VerticalStackProps {} +``` + +```sh +npx @shopify/polaris-migrator react-rename-component --renameFrom="AlphaStack" --renameTo="VerticalStack" --renamePropsFrom="AlphaStackProps" --renamePropsTo="VerticalStackProps" +``` + +#### Post-migration validation + +After migrating, search for React instances or CSS class overrides of the `AlphaStack` component across all file types in your project: + +```jsx + ++ +- export interface AlphaCardProps {} ++ export interface CardProps {} +``` + +```sh +npx @shopify/polaris-migrator react-rename-component --renameFrom="AlphaCard" --renameTo="Card" --renamePropsFrom="AlphaCardProps" --renamePropsTo="CardProps" +``` + +#### Post-migration validation + +After migrating, search for React instances or CSS class overrides of the `AlphaCard` component across all file types in your project: + +```jsx + ++ +- export interface AlphaFiltersProps {} ++ export interface FiltersProps {} +``` + +```sh +npx @shopify/polaris-migrator react-rename-component --renameFrom="AlphaFilters" --renameTo="Filters" --renamePropsFrom="AlphaFiltersProps" --renamePropsTo="FiltersProps" +``` + +#### Post-migration validation + +After migrating, search for React instances or CSS class overrides of the `AlphaFilters` component across all file types in your project: + +```jsx + ++ +- export interface AlphaTabsProps {} ++ export interface TabsProps {} +``` + +```sh +npx @shopify/polaris-migrator react-rename-component --renameFrom="AlphaTabs" --renameTo="Tabs" --renamePropsFrom="AlphaTabsProps" --renamePropsTo="TabsProps" +``` + +> Note: If the `AlphaTabProp` type is being used in the codebase, it requires an additional migration **after** `AlphaTabs` has been migrated and renamed to `Tabs`. + +To rename `AlphaTabProps` to `TabProps`, you can run the generic [react-rename-component](https://polaris.shopify.com/tools/polaris-migrator#generic-migrations) migration. Please reference the [recommended component migration workflow](#recommended-component-migration-workflow) section below for additional migration support. + +```diff +- export interface AlphaTabProps {} ++ export interface TabProps {} +``` + +```sh +npx @shopify/polaris-migrator react-rename-component --renamePropsFrom="AlphaTabProps" --renamePropsTo="TabProps" +``` + +#### Post-migration validation + +After migrating, search for React instances or CSS class overrides of the `AlphaTab` component across all file types in your project: + +```jsx + +
+ ```sh + # Stage all manually migrated files + git add . + # Format staged files only + git diff --staged --name-only | xargs npx prettier --write + # Commit manual migrations + git commit -m "Manually migrate X to Y" + ``` + +## Tokens + +The following tokens have either been renamed or removed. You will need to replace any instances of them with their new name or value equivalents. Please review each token section for migrations that can be run to resolve these breaking changes. + +### Border + +#### Migration + +To replace these deprecated `shape` custom properties, you can run the [v11-styles-replace-custom-property-border](https://polaris.shopify.com/tools/polaris-migrator#v11-styles-replace-custom-property-border) migration. Please reference the [recommended token migration workflow](#recommended-token-migration-workflow) section below for additional migration support. + +```diff +- border: var(--p-border-divider); ++ border: var(--p-border-width-1) solid var(--p-color-border-subdued); +``` + +```sh +npx @shopify/polaris-migrator v11-styles-replace-custom-property-border +``` + +#### Post-migration validation + +After migrating use the following RegExp to check for any additional instances of `shape` custom properties across all file types: + +``` +--p-border-radius-base|--p-border-radius-large|--p-border-radius-half|--p-border-base|--p-border-dark|--p-border-transparent|--p-border-divider|--p-border-divider-on-dark +``` + +``` +\w](?:[^>]|\n)*?border +``` + +#### Replacement maps + +| Deprecated Token | Replacement Value | +| ---------------------------- | ------------------------------------------------------------- | +| `--p-border-radius-base` | `--p-border-radius-1` | +| `--p-border-radius-large` | `--p-border-radius-2` | +| `--p-border-radius-half` | `--p-border-radius-full` | +| `--p-border-base` | `var(--p-border-width-1) solid var(--p-color-border-subdued)` | +| `--p-border-dark` | `var(--p-border-width-1) solid var(--p-color-border)` | +| `--p-border-transparent` | `var(--p-border-width-1) solid transparent` | +| `--p-border-divider` | `var(--p-border-width-1) solid var(--p-color-border-subdued)` | +| `--p-border-divider-on-dark` | `var(--p-border-width-1) solid var(--p-color-border-inverse)` | + +### Color + +#### Migration + +To replace these deprecated `colors` custom properties, you can run the [v11-styles-replace-custom-property-color](https://polaris.shopify.com/tools/polaris-migrator#v11-styles-replace-custom-property-color) migration. Please reference the [recommended token migration workflow](#recommended-token-migration-workflow) section below for additional migration support. + +```diff +- color: var(--p-text); ++ color: var(--p-color-text); +``` + +```sh +npx @shopify/polaris-migrator v11-styles-replace-custom-property-color +``` + +#### Post-migration validation + +After migrating use the following RegExp to check for any additional instances of `colors` custom properties across all file types: + +``` +--p-text-warning|--p-text-success|--p-text-subdued-on-dark|--p-text-subdued(?!-light|-neutral-light)|--p-text-primary-pressed|--p-text-primary-hovered|--p-text-primary|--p-text-on-primary|--p-text-on-interactive|--p-text-on-dark|--p-text-on-critical|--p-text-highlight|--p-text-disabled|--p-text-critical|--p-text(?!-field-spinner|-light|-subdued-light)|--p-surface-warning-subdued-pressed|--p-surface-warning-subdued-hovered|--p-surface-warning-subdued|--p-surface-warning|--p-surface-success-subdued-pressed|--p-surface-success-subdued-hovered|--p-surface-success-subdued|--p-surface-success|--p-surface-subdued|--p-surface-selected-pressed|--p-surface-selected-hovered|--p-surface-selected|--p-surface-search-field-dark|--p-surface-search-field|--p-surface-primary-selected-pressed|--p-surface-primary-selected-hovered|--p-surface-primary-selected|--p-surface-pressed-dark|--p-surface-pressed|--p-surface-neutral-subdued-dark|--p-surface-neutral-subdued|--p-surface-neutral-pressed|--p-surface-neutral-hovered|--p-surface-neutral-disabled|--p-surface-neutral(?!-light)|--p-surface-hovered-dark|--p-surface-hovered(?!-light)|--p-surface-highlight-subdued-pressed|--p-surface-highlight-subdued-hovered|--p-surface-highlight-subdued|--p-surface-highlight|--p-surface-disabled|--p-surface-depressed|--p-surface-dark|--p-surface-critical-subdued-pressed|--p-surface-critical-subdued-hovered|--p-surface-critical-subdued-depressed|--p-surface-critical-subdued|--p-surface-critical|--p-surface-attention|--p-surface(?!-hover|-interactive|-primary|-light|-neutral-light)|--p-shadow-color-picker-dragger|--p-shadow-color-picker|--p-overlay|--p-interactive-pressed-on-dark|--p-interactive-pressed|--p-interactive-on-dark|--p-interactive-hovered|--p-interactive-disabled|--p-interactive-critical-pressed|--p-interactive-critical-hovered|--p-interactive-critical-disabled|--p-interactive-critical|--p-interactive|--p-icon-warning|--p-icon-success|--p-icon-subdued|--p-icon-pressed|--p-icon-on-primary|--p-icon-on-interactive|--p-icon-on-dark|--p-icon-on-critical|--p-icon-hovered|--p-icon-highlight(?!--light)|--p-icon-disabled|--p-icon-critical|--p-icon-attention|--p-icon(?!-pinned|-hover|-light|-highlight--light)|--p-hint-from-direct-light|--p-focused|--p-divider-dark|--p-divider|--p-decorative-two-text|--p-decorative-two-surface|--p-decorative-two-icon|--p-decorative-three-text|--p-decorative-three-surface|--p-decorative-three-icon|--p-decorative-one-text|--p-decorative-one-surface|--p-decorative-one-icon|--p-decorative-four-text|--p-decorative-four-surface|--p-decorative-four-icon|--p-decorative-five-text|--p-decorative-five-surface|--p-decorative-five-icon|--p-border-warning-subdued|--p-border-warning|--p-border-success-subdued|--p-border-success|--p-border-subdued|--p-border-shadow-subdued|--p-border-shadow|--p-border-on-dark|--p-border-neutral-subdued|--p-border-hovered|--p-border-highlight-subdued|--p-border-highlight|--p-border-disabled|--p-border-depressed|--p-border-critical-subdued|--p-border-critical-disabled|--p-border-critical|--p-border(?!-width|-radius|-base|-dark|-transparent|-divider|-divider-on-dark)|--p-background-selected|--p-background-pressed|--p-background-hovered|--p-background|--p-backdrop|--p-action-secondary-pressed-dark|--p-action-secondary-pressed|--p-action-secondary-hovered-dark|--p-action-secondary-hovered|--p-action-secondary-disabled|--p-action-secondary-depressed|--p-action-secondary|--p-action-primary-pressed|--p-action-primary-hovered|--p-action-primary-disabled|--p-action-primary-depressed|--p-action-primary|--p-action-critical-pressed|--p-action-critical-hovered|--p-action-critical-disabled|--p-action-critical-depressed|--p-action-critical +``` + +``` +\w](?:[^>]|\n)*?background(?!="bg) +``` + +``` +\w](?:[^>]|\n)*?background(?!="bg) +``` + +``` +\w](?:[^>]|\n)*?color(?!="text) +``` + +``` +\w](?:[^>]|\n)*?borderColor(?!="border) +``` + +``` +\w](?:[^>]|\n)*?outlineColor(?!="border) +``` + +``` +\w](?:[^>]|\n)*?borderColor(?!="border) +``` + +#### Replacement maps + +| Deprecated Token | Replacement Value | +| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `--p-text-warning` | `--p-color-text-caution` | +| `--p-text-success` | `--p-color-text-success` | +| `--p-text-subdued-on-dark` | `--p-color-text-inverse-subdued` | +| `--p-text-subdued` | `--p-color-text-subdued` | +| `--p-text-primary-pressed` | `--p-color-text-primary` | +| `--p-text-primary-hovered` | `--p-color-text-primary-hover` | +| `--p-text-primary` | `--p-color-text-primary` | +| `--p-text-on-primary` | `--p-color-text-on-color` | +| `--p-text-on-interactive` | `--p-color-text-on-color` | +| `--p-text-on-dark` | `--p-color-text-inverse` | +| `--p-text-on-critical` | `--p-color-text-on-color` | +| `--p-text-highlight` | `--p-color-text-info` | +| `--p-text-disabled` | `--p-color-text-disabled` | +| `--p-text-critical` | `--p-color-text-critical` | +| `--p-text` | `--p-color-text` | +| `--p-surface-warning-subdued-pressed` | `--p-color-bg-caution-subdued-active` | +| `--p-surface-warning-subdued-hovered` | `--p-color-bg-caution-subdued-hover` | +| `--p-surface-warning-subdued` | `--p-color-bg-caution-subdued` | +| `--p-surface-warning` | `--p-color-bg-warning` | +| `--p-surface-success-subdued-pressed` | `--p-color-bg-success-subdued-active` | +| `--p-surface-success-subdued-hovered` | `--p-color-bg-success-subdued-hover` | +| `--p-surface-success-subdued ` | `--p-color-bg-success-subdued` | +| `--p-surface-success` | `--p-color-bg-success` | +| `--p-surface-subdued` | `--p-color-bg-subdued` | +| `--p-surface-selected-pressed` | `--p-color-bg-interactive-subdued-active` | +| `--p-surface-selected-hovered` | `--p-color-bg-interactive-subdued-hover` | +| `--p-surface-selected` | `--p-color-bg-interactive-selected` | +| `--p-surface-search-field-dark` | `--p-color-bg-inverse` | +| `--p-surface-search-field` | `--p-color-bg-inset` | +| `--p-surface-primary-selected-pressed` | `--p-color-bg-primary-subdued-active` | +| `--p-surface-primary-selected-hovered` | `--p-color-bg-primary-subdued-hover` | +| `--p-surface-primary-selected` | `--p-color-bg-primary-subdued-selected` | +| `--p-surface-pressed-dark` | `--p-color-bg-inverse-active` | +| `--p-surface-pressed` | `--p-color-bg-active` | +| `--p-surface-neutral-subdued-dark` | `--p-color-bg-inverse` | +| `--p-surface-neutral-subdued` | `--p-color-bg-subdued` | +| `--p-surface-neutral-pressed` | `--p-color-bg-strong-active` | +| `--p-surface-neutral-hovered` | `--p-color-bg-strong-hover` | +| `--p-surface-neutral-disabled` | `--p-color-bg-disabled` | +| `--p-surface-neutral` | `--p-color-bg-strong` | +| `--p-surface-hovered-dark` | `--p-color-bg-inverse-hover` | +| `--p-surface-hovered` | `--p-color-bg-hover` | +| `--p-surface-highlight-subdued-pressed` | `--p-color-bg-info-subdued-active` | +| `--p-surface-highlight-subdued-hovered` | `--p-color-bg-info-subdued-hover` | +| `--p-surface-highlight-subdued` | `--p-color-bg-info-subdued` | +| `--p-surface-highlight` | `--p-color-bg-info` | +| `--p-surface-disabled` | `--p-color-bg-disabled` | +| `--p-surface-depressed` | `--p-color-bg-inset` | +| `--p-surface-dark` | `--p-color-bg-inverse` | +| `--p-surface-critical-subdued-pressed` | `--p-color-bg-critical-subdued-active` | +| `--p-surface-critical-subdued-hovered` | `--p-color-bg-critical-subdued-hover` | +| `--p-surface-critical-subdued-depressed` | `--p-color-bg-critical` | +| `--p-surface-critical-subdued` | `--p-color-bg-critical-subdued` | +| `--p-surface-critical` | `--p-color-bg-critical` | +| `--p-surface-attention` | `--p-color-bg-caution` | +| `--p-surface` | `--p-color-bg` | +| `--p-shadow-color-picker-dragger` | `rgba(33, 43, 54, 0.32)` | +| `--p-shadow-color-picker` | `rgba(0, 0, 0, 0.5)` | +| `--p-overlay` | `rgba(255, 255, 255, 0.5)` | +| `--p-interactive-pressed-on-dark` | `--p-color-text-interactive-inverse` | +| `--p-interactive-pressed` | For `color` properties: `--p-color-text-interactive-active`
For `background` properties: `--p-color-bg-interactive-active`
For `border` properties: `--p-color-border-interactive-active`
For `fill` properties: `--p-color-icon-interactive-active` | +| `--p-interactive-on-dark` | `--p-color-text-interactive-inverse` | +| `--p-interactive-hovered` | For `color` properties: `--p-color-text-interactive-hover`
For `background` properties: `--p-color-bg-interactive-hover`
For `border` properties: `--p-color-border-interactive-hover`
For `fill` properties: `--p-color-icon-interactive-hover` | +| `--p-interactive-disabled` | For `color` properties: `--p-color-text-interactive-disabled`
For `background` properties: `--p-color-bg-interactive-disabled`
For `border` properties: `--p-color-border-interactive-disabled`
For `fill` properties: `--p-color-icon-interactive-disabled` | +| `--p-interactive-critical-pressed` | `--p-color-text-critical-active` | +| `--p-interactive-critical-hovered` | `--p-color-bg-critical-strong-hover` | +| `--p-interactive-critical-disabled` | `--p-color-text-disabled` | +| `--p-interactive-critical` | For `color` properties: `--p-color-text-critical`
For `background` properties: `--p-color-bg-critical`
For `border` properties: `--p-color-border-critical`
For `fill` properties: `--p-color-icon-critical` | +| `--p-interactive` | For `color` properties: `--p-color-text-interactive`
For `background` properties: `--p-color-bg-interactive`
For `border` properties: `--p-color-border-interactive`
For `fill` properties: `--p-color-icon-interactive` | +| `--p-icon-warning` | `--p-color-icon-caution` | +| `--p-icon-success` | `--p-color-icon-success` | +| `--p-icon-subdued` | `--p-color-icon-subdued` | +| `--p-icon-pressed` | `--p-color-icon-active` | +| `--p-icon-on-primary` | `--p-color-icon-on-color` | +| `--p-icon-on-interactive` | `--p-color-icon-on-color` | +| `--p-icon-on-dark` | `--p-color-icon-inverse` | +| `--p-icon-on-critical` | `--p-color-icon-on-color` | +| `--p-icon-hovered` | `--p-color-icon-hover` | +| `--p-icon-highlight` | `--p-color-icon-info` | +| `--p-icon-disabled` | `--p-color-icon-disabled` | +| `--p-icon-critical` | `--p-color-icon-critical` | +| `--p-icon-attention` | `--p-color-icon-caution` | +| `--p-icon` | `--p-color-icon` | +| `--p-hint-from-direct-light` | `rgba(0, 0, 0, 0.15)` | +| `--p-focused` | `--p-color-border-interactive-focus` | +| `--p-divider-dark` | `--p-color-border-inverse` | +| `--p-divider` | `--p-color-border-subdued` | +| `--p-decorative-two-text` | `rgba(73, 11, 28, 1)` | +| `--p-decorative-two-surface` | `rgba(255, 196, 176, 1)` | +| `--p-decorative-two-icon` | `rgba(175, 41, 78, 1)` | +| `--p-decorative-three-text` | `rgba(0, 47, 25, 1)` | +| `--p-decorative-three-surface` | `rgba(146, 230, 181, 1)` | +| `--p-decorative-three-icon` | `rgba(0, 109, 65, 1)` | +| `--p-decorative-one-text` | `rgba(61, 40, 0, 1)` | +| `--p-decorative-one-surface` | `rgba(255, 201, 107, 1)` | +| `--p-decorative-one-icon` | `rgba(126, 87, 0, 1)` | +| `--p-decorative-four-text` | `rgba(0, 45, 45, 1)` | +| `--p-decorative-four-surface` | `rgba(145, 224, 214, 1)` | +| `--p-decorative-four-icon` | `rgba(0, 106, 104, 1)` | +| `--p-decorative-five-text` | `rgba(79, 14, 31, 1)` | +| `--p-decorative-five-surface` | `rgba(253, 201, 208, 1)` | +| `--p-decorative-five-icon` | `rgba(174, 43, 76, 1)` | +| `--p-border-warning-subdued` | `--p-color-border-caution-subdued` | +| `--p-border-warning` | `--p-color-border-caution` | +| `--p-border-success-subdued` | `--p-color-border-success-subdued` | +| `--p-border-success` | `--p-color-border-success` | +| `--p-border-subdued` | `--p-color-border-subdued` | +| `--p-border-shadow-subdued` | `--p-color-border-input` | +| `--p-border-shadow` | `--p-color-border-input` | +| `--p-border-on-dark` | `--p-color-border-inverse` | +| `--p-border-neutral-subdued` | `--p-color-border-strong` | +| `--p-border-hovered` | `--p-color-border-hover` | +| `--p-border-highlight-subdued` | `--p-color-border-info-subdued` | +| `--p-border-highlight` | `--p-color-border-info` | +| `--p-border-disabled` | `--p-color-border-disabled` | +| `--p-border-depressed` | `--p-color-border-inverse` | +| `--p-border-critical-subdued` | `--p-color-border-critical-subdued` | +| `--p-border-critical-disabled` | `--p-color-border-disabled` | +| `--p-border-critical` | `--p-color-border-critical` | +| `--p-border` | `--p-color-border` | +| `--p-background-selected` | `--p-color-bg-app-selected` | +| `--p-background-pressed` | `--p-color-bg-app-active` | +| `--p-background-hovered` | `--p-color-bg-app-hover` | +| `--p-background` | `--p-color-bg-app` | +| `--p-backdrop` | `rgba(0, 0, 0, 0.5)` | +| `--p-action-secondary-pressed-dark` | `--p-color-bg-inverse-active` | +| `--p-action-secondary-pressed` | `--p-color-bg-subdued-active` | +| `--p-action-secondary-hovered-dark` | `--p-color-bg-inverse-hover` | +| `--p-action-secondary-hovered` | `--p-color-bg-subdued-hover` | +| `--p-action-secondary-disabled` | `--p-color-bg-disabled` | +| `--p-action-secondary-depressed` | `--p-color-bg-inset-strong` | +| `--p-action-secondary` | `--p-color-bg-subdued` | +| `--p-action-primary-pressed` | `--p-color-bg-primary-active` | +| `--p-action-primary-hovered` | `--p-color-bg-primary-hover` | +| `--p-action-primary-disabled` | `--p-color-bg-disabled` | +| `--p-action-primary-depressed` | `--p-color-bg-primary-active` | +| `--p-action-primary` | For `color` properties: `--p-color-text-primary`
For `background` properties: `--p-color-bg-primary`
For `border` properties: `--p-color-border-primary`
For `fill` properties: `--p-color-icon-primary` | +| `--p-action-critical-pressed` | `--p-color-bg-critical-strong-active` | +| `--p-action-critical-hovered` | `--p-color-bg-critical-strong-hover` | +| `--p-action-critical-disabled` | `--p-color-bg-disabled` | +| `--p-action-critical-depressed` | `--p-color-bg-critical-strong-active` | +| `--p-action-critical` | `--p-color-bg-critical-strong` | + +### Legacy + +#### Migration + +To replace these deprecated `legacy` custom properties, you can run the [v11-styles-replace-custom-property-legacy](https://polaris.shopify.com/tools/polaris-migrator#v11-styles-replace-custom-property-legacy) migration. Please reference the [recommended token migration workflow](#recommended-token-migration-workflow) section below for additional migration support. + +```diff +- z-index: var(--p-override-loading-z-index); ++ z-index: var(--p-z-index-6); + +- width: var(--p-choice-size); ++ width: 20px; +``` + +```sh +npx @shopify/polaris-migrator v11-styles-replace-custom-property-legacy +``` + +#### Post-migration validation + +After migrating use the following RegExp to check for any additional instances of `legacy` custom properties across all file types: + +``` +--p-banner-border-critical|--p-banner-border-default|--p-banner-border-highlight|--p-banner-border-success|--p-banner-border-warning|--p-button-group-item-spacing|--p-choice-margin|--p-choice-size|--p-control-border-width|--p-frame-offset|--p-icon-size-medium|--p-icon-size-small|--p-override-loading-z-index|--p-range-slider-thumb-size-active|--p-range-slider-thumb-size-base|--p-text-field-focus-ring-offset|--p-text-field-spinner-offset|--p-thin-border-subdued +``` + +#### Replacement maps + +| Deprecated Token | Replacement Value | +| ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | +| `--p-override-loading-z-index` | `--p-z-index-6` | +| `--p-choice-size` | `20px` / `1.25rem` | +| `--p-icon-size-small` | `8px` / `0.5rem` | +| `--p-icon-size-medium` | `20px` / `1.25rem` | +| `--p-choice-margin` | `--p-space-025` | +| `--p-control-border-width` | `--p-border-width-2` | +| `--p-banner-border-default` | `inset 0 var(--p-border-width-1) 0 0 var(--p-color-border-strong), inset 0 0 0 (--p-border-width-1) var(--p-color-border-strong)` | +| `--p-banner-border-success` | `inset 0 var(--p-border-width-1) 0 0 var(--p-color-border-success-subdued), inset 0 0 0 (--p-border-width-1) var(--p-color-border-success-subdued)` | +| `--p-banner-border-highlight` | `inset 0 var(--p-border-width-1) 0 0 var(--p-color-border-info-subdued), inset 0 0 0 (--p-border-width-1) var(--p-color-border-info-subdued)` | +| `--p-banner-border-warning` | `inset 0 var(--p-border-width-1) 0 0 var(--p-color-border-caution-subdued), inset 0 0 0 (--p-border-width-1)var(--p-color-border-caution-subdued)` | +| `--p-banner-border-critical` | `inset 0 var(--p-border-width-1) 0 0 var(--p-color-border-critical-subdued), inset 0 0 0 (--p-border-width-1) var(--p-color-border-critical-subdued)` | +| `--p-thin-border-subdued` | `var(--p-border-width-1) solid var(--p-color-border-subdued)` | +| `--p-text-field-spinner-offset` | `2px` / `0.125rem` | +| `--p-text-field-focus-ring-offset` | `-4px` / `-0.25rem` | +| `--p-button-group-item-spacing` | `calc(-1 * var(--p-space-025))` | +| `--p-range-slider-thumb-size-base` | `16px` / `1rem` | +| `--p-range-slider-thumb-size-active` | `24px` / `1.5rem` | +| `--p-frame-offset` | `0` | + +### Motion + +#### Migration + +To replace these deprecated `motion` custom properties, you can run the [v11-styles-replace-custom-property-motion](https://polaris.shopify.com/tools/polaris-migrator#v11-styles-replace-custom-property-motion) migration. Please reference the [recommended token migration workflow](#recommended-token-migration-workflow) section below for additional migration support. + +```diff +- transition-timing-function: var(--p-linear); ++ transition-timing-function: var(--p-motion-linear); +``` + +```sh +npx @shopify/polaris-migrator v11-styles-replace-custom-property-motion +``` + +#### Post-migration validation + +After migrating use the following RegExp to check for any additional instances of `motion` custom properties across all file types: + +``` +--p-duration-0|--p-duration-50|--p-duration-100|--p-duration-150|--p-duration-200|--p-duration-250|--p-duration-300|--p-duration-350|--p-duration-400|--p-duration-450|--p-duration-500|--p-duration-5000|--p-ease|--p-ease-in|--p-ease-out|--p-ease-in-out|--p-linear|--p-keyframes-bounce|--p-keyframes-fade-in|--p-keyframes-pulse|--p-keyframes-spin|--p-keyframes-appear-above|--p-keyframes-appear-below +``` + +#### Replacement maps + +| Deprecated Token | Replacement Value | +| ---------------------------- | ----------------------------------- | +| `--p-linear` | `--p-motion-linear` | +| `--p-ease-in-out` | `--p-motion-ease-in-out` | +| `--p-ease-out` | `--p-motion-ease-out` | +| `--p-ease-in` | `--p-motion-ease-in` | +| `--p-ease` | `--p-motion-ease` | +| `--p-duration-0` | `--p-motion-duration-0` | +| `--p-duration-50` | `--p-motion-duration-50` | +| `--p-duration-100` | `--p-motion-duration-100` | +| `--p-duration-150` | `--p-motion-duration-150` | +| `--p-duration-200` | `--p-motion-duration-200` | +| `--p-duration-250` | `--p-motion-duration-250` | +| `--p-duration-300` | `--p-motion-duration-300` | +| `--p-duration-350` | `--p-motion-duration-350` | +| `--p-duration-400` | `--p-motion-duration-400` | +| `--p-duration-450` | `--p-motion-duration-450` | +| `--p-duration-500` | `--p-motion-duration-500` | +| `--p-duration-5000` | `--p-motion-duration-5000` | +| `--p-keyframes-bounce` | `--p-motion-keyframes-bounce` | +| `--p-keyframes-fade-in` | `--p-motion-keyframes-fade-in` | +| `--p-keyframes-pulse` | `--p-motion-keyframes-pulse` | +| `--p-keyframes-spin` | `--p-motion-keyframes-spin` | +| `--p-keyframes-appear-above` | `--p-motion-keyframes-appear-above` | +| `--p-keyframes-appear-below` | `--p-motion-keyframes-appear-below` | + +### Shadow + +#### Migration + +To replace these deprecated `depth` custom properties, you can run the [v11-styles-replace-custom-property-depth](https://polaris.shopify.com/tools/polaris-migrator#v11-styles-replace-custom-property-depth) migration. Please reference the [recommended token migration workflow](#recommended-token-migration-workflow) section below for additional migration support. + +```diff +- box-shadow: var(--p-shadow-transparent); ++ box-shadow: 0 0 0 0 transparent; +``` + +```sh +npx @shopify/polaris-migrator v11-styles-replace-custom-property-depth +``` + +#### Post-migration validation + +After migrating use the following RegExp to check for any additional instances of `depth` custom properties across all file types: + +``` +--p-shadow-base|--p-shadow-button|--p-shadow-card|--p-shadow-deep|--p-shadow-faint|--p-shadow-layer|--p-shadow-modal|--p-shadow-popover|--p-shadow-top-bar|--p-shadow-transparent|--p-shadows-inset-button|--p-shadows-inset-button-pressed +``` + +``` +\w](?:[^>]|\n)*?border +``` + +#### Replacement maps + +| Deprecated Token | Replacement Value | +| ---------------------------------- | --------------------- | +| `--p-shadow-transparent` | `--p-shadow-none` | +| `--p-shadow-faint` | `--p-shadow-sm` | +| `--p-shadow-base` | `--p-shadow-md` | +| `--p-shadow-deep` | `--p-shadow-md` | +| `--p-shadow-button` | `--p-shadow-sm` | +| `--p-shadow-top-bar` | `--p-shadow-sm` | +| `--p-shadow-card` | `--p-shadow-md` | +| `--p-shadow-popover` | `--p-shadow-xl` | +| `--p-shadow-layer` | `--p-shadow-2xl` | +| `--p-shadow-modal` | `--p-shadow-2xl` | +| `--p-shadows-inset-button` | `--p-shadow-none` | +| `--p-shadows-inset-button-pressed` | `--p-shadow-inset-md` | + +### Z-index + +#### Migration + +To replace these deprecated `z-index` custom properties, you can run the [v11-styles-replace-custom-property-zindex](https://polaris.shopify.com/tools/polaris-migrator#v11-styles-replace-custom-property-zindex) migration. Please reference the [recommended token migration workflow](#recommended-token-migration-workflow) section below for additional migration support. + +```diff +- z-index: var(--p-z-1); ++ z-index: var(--p-z-index-1); +``` + +```sh +npx @shopify/polaris-migrator v11-styles-replace-custom-property-zindex +``` + +#### Post-migration validation + +After migrating use the following RegExp to check for any additional instances of `z-index` custom properties across all file types: + +``` +--p-z-1|--p-z-2|--p-z-3|--p-z-4|--p-z-5|--p-z-6|--p-z-7|--p-z-8|--p-z-9|--p-z-10|--p-z-11|--p-z-12 +``` + +#### Replacement maps + +| Deprecated Token | Replacement Value | +| ---------------- | ----------------- | +| `--p-z-1` | `--p-z-index-1` | +| `--p-z-2` | `--p-z-index-2` | +| `--p-z-3` | `--p-z-index-3` | +| `--p-z-4` | `--p-z-index-4` | +| `--p-z-5` | `--p-z-index-5` | +| `--p-z-6` | `--p-z-index-6` | +| `--p-z-7` | `--p-z-index-7` | +| `--p-z-8` | `--p-z-index-8` | +| `--p-z-9` | `--p-z-index-9` | +| `--p-z-10` | `--p-z-index-10` | +| `--p-z-11` | `--p-z-index-11` | +| `--p-z-12` | `--p-z-index-12` | + +### Recommended token migration workflow + +When running token migrations we suggest the following workflow: + +- Handle automated migrations + ```sh + # Example migration + npx @shopify/polaris-migrator ... + # Stage all migrated files + git add . + # Format staged files only + git diff --staged --name-only | xargs npx prettier --write + # Commit automated migrations + git commit -m "Migrate X custom properties from Polaris v10 to v11" + ``` +- Handle manual migrations + - Search for token RegExps and handle manual migrations +
+ +```sh +# Stage all manually migrated files +git add . +# Format staged files only +git diff --staged --name-only | xargs npx prettier --write +# Commit manual migrations +git commit -m "Manually migrate X custom properties from Polaris v10 to v11" +``` + +- Optionally if you use `stylelint-polaris`, you can check for errors after all custom property migrations are finished + ```sh + npx stylelint + ``` diff --git a/package.json b/package.json index bb0978c6012..9c73e778dde 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "private": true, "engines": { - "node": "^14.17.0 || ^16.13.0" + "node": "^16.17.0 || >=18.12.0" }, "workspaces": { "packages": [ @@ -45,28 +45,31 @@ "postinstall": "patch-package" }, "devDependencies": { - "@babel/core": "^7.15.0", - "@babel/node": "^7.14.9", + "@babel/core": "^7.20.12", + "@babel/node": "^7.20.7", + "@babel/preset-env": "^7.16.4", + "@babel/preset-react": "^7.16.0", + "@babel/preset-typescript": "^7.18.6", "@changesets/changelog-github": "^0.4.4", "@changesets/cli": "^2.23.0", "@changesets/get-release-plan": "^3.0.14", "@next/eslint-plugin-next": "^12.1.4", - "@rollup/plugin-babel": "^5.3.1", - "@rollup/plugin-commonjs": "^21.1.0", - "@rollup/plugin-image": "^2.0.5", - "@rollup/plugin-json": "^4.1.0", - "@rollup/plugin-node-resolve": "^13.2.1", - "@rollup/plugin-replace": "^2.3.3", - "@rollup/plugin-virtual": "^2.0.3", - "@rollup/pluginutils": "^4.1.0", - "@shopify/babel-preset": "^24.1.2", + "@rollup/plugin-babel": "^6.0.3", + "@rollup/plugin-commonjs": "^24.0.1", + "@rollup/plugin-image": "^3.0.2", + "@rollup/plugin-json": "^6.0.0", + "@rollup/plugin-node-resolve": "^15.0.1", + "@rollup/plugin-replace": "^5.0.2", + "@rollup/plugin-virtual": "^3.0.1", + "@rollup/pluginutils": "^5.0.2", "@shopify/cli": "^3.10.1", "@shopify/eslint-plugin": "^42.0.1", "@shopify/stylelint-plugin": "^11.0.0", "@shopify/typescript-configs": "^5.1.0", "@size-limit/preset-small-lib": "^5.0.3", "@types/jest": "^27.5.1", - "downlevel-dts": "^0.11.0", + "@types/node": "^18.11.18", + "babel-loader": "^9.1.2", "eslint": "^8.3.0", "execa": "^5.0.0", "jest": "^27.5.1", @@ -76,13 +79,13 @@ "patch-package": "^6.4.7", "postinstall-postinstall": "^2.1.0", "prettier": "^2.5.0", - "rollup": "^2.70.2", - "rollup-plugin-node-externals": "^4.0.0", + "rollup": "^3.12.0", + "rollup-plugin-node-externals": "^5.1.0", "size-limit": "^5.0.3", "stylelint": "^14.15.0", "ts-node": "^10.7.0", "turbo": "^1.8.8", - "typescript": "^4.6.3" + "typescript": "^4.9.3" }, "size-limit": [ { diff --git a/polaris-cli/package.json b/polaris-cli/package.json index c45a7691c30..b36846538ee 100644 --- a/polaris-cli/package.json +++ b/polaris-cli/package.json @@ -26,12 +26,9 @@ "@oclif/core": "^1.13.10", "@shopify/polaris-migrator": "^0.18.2" }, - "devDependencies": { - "typescript": "^4.7.4" - }, "engine-strict": true, "engines": { - "node": ">=14.13.1" + "node": "^16.17.0 || >=18.12.0" }, "os": [ "darwin", diff --git a/polaris-codemods/src/codemods/v9-scss-replace-color/transform.ts b/polaris-codemods/src/codemods/v9-scss-replace-color/transform.ts index 2a69c7a1cfe..6bf657f7165 100644 --- a/polaris-codemods/src/codemods/v9-scss-replace-color/transform.ts +++ b/polaris-codemods/src/codemods/v9-scss-replace-color/transform.ts @@ -2,7 +2,8 @@ import type {FileInfo, API, Options} from 'jscodeshift'; import type {Plugin} from 'postcss'; import postcss from 'postcss'; import valueParser from 'postcss-value-parser'; -import {colors as tokenColors, createVar} from '@shopify/polaris-tokens'; +import {colors as tokenColors} from './v10-legacy-colors'; +import {createVar} from '@shopify/polaris-tokens'; import type {NamespaceOptions} from '../../utilities/sass'; import { diff --git a/polaris-tokens/src/token-groups/colors.ts b/polaris-codemods/src/codemods/v9-scss-replace-color/v10-legacy-colors.ts similarity index 84% rename from polaris-tokens/src/token-groups/colors.ts rename to polaris-codemods/src/codemods/v9-scss-replace-color/v10-legacy-colors.ts index 7f76aa17b3b..0b3c16e379b 100644 --- a/polaris-tokens/src/token-groups/colors.ts +++ b/polaris-codemods/src/codemods/v9-scss-replace-color/v10-legacy-colors.ts @@ -1,172 +1,4 @@ -import type {MetadataProperties} from '../types'; - -export type ColorsBackgroundTokenAlias = - | 'background' - | 'background-hovered' - | 'background-pressed' - | 'background-selected'; - -export type ColorsActionTokenAlias = - | 'action-critical' - | 'action-critical-depressed' - | 'action-critical-disabled' - | 'action-critical-hovered' - | 'action-critical-pressed' - | 'action-primary' - | 'action-primary-depressed' - | 'action-primary-disabled' - | 'action-primary-hovered' - | 'action-primary-pressed' - | 'action-secondary' - | 'action-secondary-depressed' - | 'action-secondary-disabled' - | 'action-secondary-hovered' - | 'action-secondary-hovered-dark' - | 'action-secondary-pressed' - | 'action-secondary-pressed-dark'; - -export type ColorsSurfaceTokenAlias = - | 'surface' - | 'surface-attention' - | 'surface-critical' - | 'surface-critical-subdued' - | 'surface-critical-subdued-depressed' - | 'surface-critical-subdued-hovered' - | 'surface-critical-subdued-pressed' - | 'surface-dark' - | 'surface-depressed' - | 'surface-disabled' - | 'surface-highlight' - | 'surface-highlight-subdued' - | 'surface-highlight-subdued-hovered' - | 'surface-highlight-subdued-pressed' - | 'surface-hovered' - | 'surface-hovered-dark' - | 'surface-neutral' - | 'surface-neutral-disabled' - | 'surface-neutral-hovered' - | 'surface-neutral-pressed' - | 'surface-neutral-subdued' - | 'surface-neutral-subdued-dark' - | 'surface-pressed' - | 'surface-pressed-dark' - | 'surface-primary-selected' - | 'surface-primary-selected-hovered' - | 'surface-primary-selected-pressed' - | 'surface-search-field' - | 'surface-search-field-dark' - | 'surface-selected' - | 'surface-selected-hovered' - | 'surface-selected-pressed' - | 'surface-subdued' - | 'surface-success' - | 'surface-success-subdued' - | 'surface-success-subdued-hovered' - | 'surface-success-subdued-pressed' - | 'surface-warning' - | 'surface-warning-subdued' - | 'surface-warning-subdued-hovered' - | 'surface-warning-subdued-pressed'; - -export type ColorsBackdropTokenAlias = 'backdrop'; - -export type ColorsOverlayTokenAlias = 'overlay'; - -export type ColorsBorderTokenAlias = - | 'border' - | 'border-on-dark' - | 'border-neutral-subdued' - | 'border-hovered' - | 'border-disabled' - | 'border-subdued' - | 'border-depressed' - | 'border-shadow' - | 'border-shadow-subdued' - | 'border-critical' - | 'border-critical-subdued' - | 'border-critical-disabled' - | 'border-warning' - | 'border-warning-subdued' - | 'border-highlight' - | 'border-highlight-subdued' - | 'border-success' - | 'border-success-subdued'; - -export type ColorsTokenName = - | ColorsBackgroundTokenAlias - | ColorsActionTokenAlias - | ColorsSurfaceTokenAlias - | ColorsBackdropTokenAlias - | ColorsOverlayTokenAlias - | ColorsBorderTokenAlias - | 'decorative-five-icon' - | 'decorative-five-surface' - | 'decorative-five-text' - | 'decorative-four-icon' - | 'decorative-four-surface' - | 'decorative-four-text' - | 'decorative-one-icon' - | 'decorative-one-surface' - | 'decorative-one-text' - | 'decorative-three-icon' - | 'decorative-three-surface' - | 'decorative-three-text' - | 'decorative-two-icon' - | 'decorative-two-surface' - | 'decorative-two-text' - | 'divider-dark' - | 'divider' - | 'focused' - | 'hint-from-direct-light' - | 'icon-attention' - | 'icon-critical' - | 'icon-disabled' - | 'icon-highlight' - | 'icon-hovered' - | 'icon-on-critical' - | 'icon-on-dark' - | 'icon-on-interactive' - | 'icon-on-primary' - | 'icon-pressed' - | 'icon-subdued' - | 'icon-success' - | 'icon-warning' - | 'icon' - | 'interactive-critical-disabled' - | 'interactive-critical-hovered' - | 'interactive-critical-pressed' - | 'interactive-critical' - | 'interactive-disabled' - | 'interactive-hovered' - | 'interactive-on-dark' - | 'interactive-pressed-on-dark' - | 'interactive-pressed' - | 'interactive' - | 'shadow-color-picker-dragger' - | 'shadow-color-picker' - | 'text' - | 'text-critical' - | 'text-disabled' - | 'text-highlight' - | 'text-on-critical' - | 'text-on-dark' - | 'text-on-interactive' - | 'text-on-primary' - | 'text-primary-hovered' - | 'text-primary-pressed' - | 'text-primary' - | 'text-subdued-on-dark' - | 'text-subdued' - | 'text-success' - | 'text-warning'; - -export type ColorsTokenGroup = { - [TokenName in ColorsTokenName]: string; -}; - -export const colors: { - [TokenName in ColorsTokenName]: MetadataProperties; -} = { +export const colors = { background: { value: 'rgba(246, 246, 247, 1)', description: diff --git a/polaris-for-vscode/package.json b/polaris-for-vscode/package.json index 72d65cebdb4..eb6fcf02504 100644 --- a/polaris-for-vscode/package.json +++ b/polaris-for-vscode/package.json @@ -20,7 +20,8 @@ "shopify" ], "engines": { - "vscode": "^1.64.0" + "vscode": "^1.64.0", + "node": "^16.17.0 || >=18.12.0" }, "categories": [ "Other" diff --git a/polaris-for-vscode/src/server.ts b/polaris-for-vscode/src/server.ts index 3adf4caedd5..c467521d8bc 100644 --- a/polaris-for-vscode/src/server.ts +++ b/polaris-for-vscode/src/server.ts @@ -15,13 +15,7 @@ import type { } from 'vscode-languageserver/node'; import {TextDocument} from 'vscode-languageserver-textdocument'; -const excludedTokenGroupNames = [ - 'colors', - 'depth', - 'legacy', - 'spacing', - 'shape', -] as const; +const excludedTokenGroupNames = [] as const; type ExcludedTokenGroupName = typeof excludedTokenGroupNames[number]; diff --git a/polaris-icons/package.json b/polaris-icons/package.json index 1ba6b611e70..16dc872a3be 100644 --- a/polaris-icons/package.json +++ b/polaris-icons/package.json @@ -13,6 +13,9 @@ ] } }, + "engines": { + "node": "^16.17.0 || >=18.12.0" + }, "scripts": { "build": "rollup -c", "dev": "rollup -c -w", diff --git a/polaris-icons/rollup.config.mjs b/polaris-icons/rollup.config.mjs index d0815f2729b..9448e91b439 100644 --- a/polaris-icons/rollup.config.mjs +++ b/polaris-icons/rollup.config.mjs @@ -222,6 +222,11 @@ export default [ interop, entryFileNames: '[name].js', chunkFileNames: '[name].js', + manualChunks: (id) => { + if (id.startsWith(iconBasePath)) { + return id.replace(iconBasePath, 'icons/'); + } + }, }, { dir: 'dist', @@ -229,17 +234,14 @@ export default [ interop, entryFileNames: '[name].mjs', chunkFileNames: '[name].mjs', + manualChunks: (id) => { + if (id.startsWith(iconBasePath)) { + return id.replace(iconBasePath, 'icons/'); + } + }, }, ], - manualChunks: (id) => { - // Generate distinct chunks for each icon - // This allows consuming apps to split up the icons into multiple subchunks - // containing a few icons each instead of always having to put every icon - // into a single shared chunk - if (id.startsWith(iconBasePath)) { - return id.replace(iconBasePath, 'icons/'); - } - }, + external: ['react'], onwarn: (warning, warn) => { // Unresolved imports means Rollup couldn't find an import, possibly because diff --git a/polaris-migrator/package.json b/polaris-migrator/package.json index 6708322dc97..065eeb13c8b 100644 --- a/polaris-migrator/package.json +++ b/polaris-migrator/package.json @@ -6,6 +6,9 @@ "author": "Shopify ", "homepage": "https://polaris.shopify.com", "repository": "https://github.com/Shopify/polaris", + "engines": { + "node": "^16.17.0 || >=18.12.0" + }, "bugs": { "url": "https://github.com/Shopify/polaris/issues" }, diff --git a/polaris-migrator/rollup.config.mjs b/polaris-migrator/rollup.config.mjs index 7590ff5f94f..5785b04448d 100644 --- a/polaris-migrator/rollup.config.mjs +++ b/polaris-migrator/rollup.config.mjs @@ -21,14 +21,14 @@ export default { output: [ { format: /** @type {const} */ ('cjs'), - entryFileNames: '[name][assetExtname].js', + entryFileNames: '[name].js', dir: path.dirname(pkg.main), preserveModules: true, exports: 'auto', }, { format: /** @type {const} */ ('esm'), - entryFileNames: '[name][assetExtname].mjs', + entryFileNames: '[name].mjs', dir: path.dirname(pkg.module), preserveModules: true, }, @@ -45,7 +45,7 @@ export default { include: ['src/**/*'], babelHelpers: 'bundled', envName: 'production', - targets: 'node 14.13', + targets: 'node 16.17.0', }), json({compact: true}), ], diff --git a/polaris-migrator/src/migrations/v9-scss-replace-color/v10-legacy-colors.ts b/polaris-migrator/src/migrations/v9-scss-replace-color/v10-legacy-colors.ts new file mode 100644 index 00000000000..0b3c16e379b --- /dev/null +++ b/polaris-migrator/src/migrations/v9-scss-replace-color/v10-legacy-colors.ts @@ -0,0 +1,647 @@ +export const colors = { + background: { + value: 'rgba(246, 246, 247, 1)', + description: + 'For use as a background color, in components such as Page and Frame backgrounds.', + }, + 'background-hovered': { + value: 'rgba(241, 242, 243, 1)', + description: + 'For use when an action or navigation is used on a background.', + }, + 'background-pressed': { + value: 'rgba(237, 238, 239, 1)', + description: + 'For use when an action or navigation is used on a background.', + }, + 'background-selected': { + value: 'rgba(237, 238, 239, 1)', + description: 'For use in the selected item in navigation', + }, + surface: { + value: 'rgba(255, 255, 255, 1)', + description: + 'For use as a background color, in components such as Card, Modal, and Popover.', + }, + 'surface-dark': { + value: 'rgba(32, 33, 35, 1)', + description: + 'For use as a dark background color, in components such as Card, Modal, and Popover.', + }, + 'surface-neutral': { + value: 'rgba(228, 229, 231, 1)', + description: 'For use as a background color in neutral badges.', + }, + 'surface-neutral-hovered': { + value: 'rgba(219, 221, 223, 1)', + description: 'For use as a hovered background color in neutral badges.', + }, + 'surface-neutral-pressed': { + value: 'rgba(201, 204, 208, 1)', + description: 'For use as a pressed background color in neutral badges.', + }, + 'surface-neutral-disabled': { + value: 'rgba(241, 242, 243, 1)', + description: 'For use as a disabled background color in neutral badges.', + }, + 'surface-neutral-subdued': { + value: 'rgba(246, 246, 247, 1)', + description: 'For use as a background color in neutral banners.', + }, + 'surface-neutral-subdued-dark': { + value: 'rgba(68, 71, 74, 1)', + description: 'For use as a dark background color in neutral banners.', + }, + 'surface-subdued': { + value: 'rgba(250, 251, 251, 1)', + description: + 'For use as a subdued background color, in components such as Card, Modal, and Popover.', + }, + 'surface-disabled': { + value: 'rgba(250, 251, 251, 1)', + description: + 'For use as a surface color on disabled interactive elements such as option list items and action list items when in a disabled state.', + }, + 'surface-hovered': { + value: 'rgba(246, 246, 247, 1)', + description: + 'For use as a surface color on interactive elements such as resource list items and action list items when in a hovered state.', + }, + 'surface-hovered-dark': { + value: 'rgba(47, 49, 51, 1)', + description: + 'For use as a dark surface color on interactive elements such as resource list items and action list items when in a hovered state.', + }, + 'surface-pressed': { + value: 'rgba(241, 242, 243, 1)', + description: + 'For use as a surface color on interactive elements such as resource list items and action list items when in a pressed state.', + }, + 'surface-pressed-dark': { + value: 'rgba(62, 64, 67, 1)', + description: + 'For use as a dark surface color on interactive elements such as resource list items and action list items when in a pressed state.', + }, + 'surface-depressed': { + value: 'rgba(237, 238, 239, 1)', + description: + 'For use as a surface color on interactive elements such as resource list items and action list items when in a depressed state.', + }, + 'surface-search-field': { + value: 'rgba(241, 242, 243, 1)', + description: + 'For use as a background color, in components on surface elements such as SearchField', + }, + 'surface-search-field-dark': { + value: 'rgba(47, 49, 51, 1)', + description: + 'For use as a dark background color, in components on surface elements such as SearchField', + }, + backdrop: { + value: 'rgba(0, 0, 0, 0.5)', + description: + 'For use as the background color of the backdrop component for navigation and modal. This color has an alpha of `0.5`.', + }, + overlay: { + value: 'rgba(255, 255, 255, 0.5)', + description: + 'For use as the background color of elements which lay on top of surfaces to obscure their contents. This color has an alpha of `0.5`.', + }, + 'shadow-color-picker': { + value: 'rgba(0, 0, 0, 0.5)', + }, + 'shadow-color-picker-dragger': { + value: 'rgba(33, 43, 54, 0.32)', + }, + 'hint-from-direct-light': { + value: 'rgba(0, 0, 0, 0.15)', + description: 'For use in building shadows scrollables.', + }, + border: { + value: 'rgba(140, 145, 150, 1)', + description: 'For use as the default border on elements.', + }, + 'border-on-dark': { + value: 'rgba(80, 83, 86, 1)', + description: 'For use as the default border on dark elements.', + }, + 'border-neutral-subdued': { + value: 'rgba(186, 191, 195, 1)', + description: 'For use as the border on banners.', + }, + 'border-hovered': { + value: 'rgba(153, 158, 164, 1)', + description: 'Used for borders on hovered interactive elements', + }, + 'border-disabled': { + value: 'rgba(210, 213, 216, 1)', + description: 'Used for disabled borders on interactive elements', + }, + 'border-subdued': { + value: 'rgba(201, 204, 207, 1)', + description: 'For use as a subdued border on elements.', + }, + 'border-depressed': { + value: 'rgba(87, 89, 89, 1)', + description: 'For use as a border on depressed elements.', + }, + 'border-shadow': { + value: 'rgba(174, 180, 185, 1)', + description: 'For use as an additional bottom border on elements.', + }, + 'border-shadow-subdued': { + value: 'rgba(186, 191, 196, 1)', + description: 'For use as an additional, subdued bottom border on elements.', + }, + divider: { + value: 'rgba(225, 227, 229, 1)', + description: 'For use as a divider between elements.', + }, + 'divider-dark': { + value: 'rgba(69, 71, 73, 1)', + description: 'For use as a dark divider between elements.', + }, + icon: { + value: 'rgba(92, 95, 98, 1)', + description: 'For use as the fill color of icons.', + }, + 'icon-on-dark': { + value: 'rgba(166, 172, 178, 1)', + description: 'For use as the fill color of dark icons.', + }, + 'icon-hovered': { + value: 'rgba(26, 28, 29, 1)', + description: 'For use as the fill color of hovered icons.', + }, + 'icon-pressed': { + value: 'rgba(68, 71, 74, 1)', + description: 'For use as the fill color of pressed icons.', + }, + 'icon-disabled': { + value: 'rgba(186, 190, 195, 1)', + description: 'For use as the fill color of disabled icons.', + }, + 'icon-subdued': { + value: 'rgba(140, 145, 150, 1)', + description: 'For use as the fill color of subdued icons.', + }, + text: { + value: 'rgba(32, 34, 35, 1)', + description: 'For use as a text color.', + }, + 'text-on-dark': { + value: 'rgba(227, 229, 231, 1)', + description: 'For use as a text color on dark elements.', + }, + 'text-disabled': { + value: 'rgba(140, 145, 150, 1)', + description: + 'For use as a disabled text color and as a placeholder text color.', + }, + 'text-subdued': { + value: 'rgba(109, 113, 117, 1)', + description: 'For use as a subdued text color.', + }, + 'text-subdued-on-dark': { + value: 'rgba(153, 159, 164, 1)', + description: 'For use as a subdued text color on dark elements.', + }, + interactive: { + value: 'rgba(44, 110, 203, 1)', + description: + 'Used for links, plain buttons, and as the fill color for selected checkboxes and radio buttons.', + }, + 'interactive-on-dark': { + value: 'rgba(54, 163, 255, 1)', + description: + 'Used for links, plain buttons, and as the fill color for selected checkboxes and radio buttons when on a dark element.', + }, + 'interactive-disabled': { + value: 'rgba(189, 193, 204, 1)', + description: 'Used for disabled links and plain buttons.', + }, + 'interactive-hovered': { + value: 'rgba(31, 81, 153, 1)', + description: 'Used for hovered links and plain buttons.', + }, + 'interactive-pressed': { + value: 'rgba(16, 50, 98, 1)', + description: 'Used for pressed links and plain buttons.', + }, + 'interactive-pressed-on-dark': { + value: 'rgba(136, 188, 255, 1)', + description: 'Used for pressed links and plain buttons on dark elements.', + }, + focused: { + value: 'rgba(69, 143, 255, 1)', + description: 'For use in the focus ring on interactive elements.', + }, + 'surface-selected': { + value: 'rgba(242, 247, 254, 1)', + description: + 'For use as a surface color in selected interactive elements, in components such as action list and resource list.', + }, + 'surface-selected-hovered': { + value: 'rgba(237, 244, 254, 1)', + description: + 'For use as a surface color in selected interactive elements that are hovered, in components such as action list and resource list.', + }, + 'surface-selected-pressed': { + value: 'rgba(229, 239, 253, 1)', + description: + 'For use as a surface color in selected interactive elements that are pressed, in components such as action list and resource list.', + }, + 'icon-on-interactive': { + value: 'rgba(255, 255, 255, 1)', + description: 'For use as a fill color for icons on interactive elements.', + }, + 'text-on-interactive': { + value: 'rgba(255, 255, 255, 1)', + description: 'For use as a text color on interactive elements.', + }, + 'action-secondary': { + value: 'rgba(255, 255, 255, 1)', + description: + 'Used for secondary buttons and tertiary buttons, as well as in form elements as a background color and pontentially other secondary surfaces.', + }, + 'action-secondary-disabled': { + value: 'rgba(255, 255, 255, 1)', + description: 'Used as a disabled state for secondary buttons', + }, + 'action-secondary-hovered': { + value: 'rgba(246, 246, 247, 1)', + description: 'Used as a hovered state for secondary buttons', + }, + 'action-secondary-hovered-dark': { + value: 'rgba(84, 87, 91, 1)', + description: 'Used as a dark hovered state for secondary buttons', + }, + 'action-secondary-pressed': { + value: 'rgba(241, 242, 243, 1)', + description: 'Used as a pressed state for secondary buttons', + }, + 'action-secondary-pressed-dark': { + value: 'rgba(96, 100, 103, 1)', + description: 'Used as a dark pressed state for secondary buttons', + }, + 'action-secondary-depressed': { + value: 'rgba(109, 113, 117, 1)', + description: 'Used as a depressed state for secondary buttons', + }, + 'action-primary': { + value: 'rgba(0, 128, 96, 1)', + description: + 'Used as the background color for primary actions, and as the fill color for icons and the text color in navigation and tabs to communicate interaction states.', + }, + 'action-primary-disabled': { + value: 'rgba(241, 241, 241, 1)', + description: + 'Used as the background color for disabled primary actions, and as the fill color for icons and the text color in navigation and tabs to communicate interaction states.', + }, + 'action-primary-hovered': { + value: 'rgba(0, 110, 82, 1)', + description: + 'Used as the background color for hovered primary actions, and as the fill color for icons and the text color in navigation and tabs to communicate interaction states.', + }, + 'action-primary-pressed': { + value: 'rgba(0, 94, 70, 1)', + description: + 'Used as the background color for pressed primary actions, and as the fill color for icons and the text color in navigation and tabs to communicate interaction states.', + }, + 'action-primary-depressed': { + value: 'rgba(0, 61, 44, 1)', + description: + 'Used as the background color for pressed primary actions, and as the fill color for icons and the text color in navigation and tabs to communicate interaction states.', + }, + 'icon-on-primary': { + value: 'rgba(255, 255, 255, 1)', + description: + 'For use as a fill color for icons on primary actions. Not for use in icons on navigation and tabs.', + }, + 'text-on-primary': { + value: 'rgba(255, 255, 255, 1)', + description: + 'For use as a text color on primary actions. Not for use in text on navigation and tabs.', + }, + 'text-primary': { + value: 'rgba(0, 123, 92, 1)', + description: + 'For use as primary text color on background. For use in text in components such as Navigation.', + }, + 'text-primary-hovered': { + value: 'rgba(0, 108, 80, 1)', + description: + 'For use as primary hovered text color on background. For use in text in components such as Navigation.', + }, + 'text-primary-pressed': { + value: 'rgba(0, 92, 68, 1)', + description: + 'For use as primary pressed text color on background. For use in text in components such as Navigation.', + }, + 'surface-primary-selected': { + value: 'rgba(241, 248, 245, 1)', + description: + 'Used as a surface color to indicate selected interactive states in navigation and tabs.', + }, + 'surface-primary-selected-hovered': { + value: 'rgba(179, 208, 195, 1)', + description: + 'Used as a surface color to indicate selected interactive states that are hovered in navigation and tabs.', + }, + 'surface-primary-selected-pressed': { + value: 'rgba(162, 188, 176, 1)', + description: + 'Used as a surface color to indicate selected interactive states that are pressed in navigation and tabs.', + }, + 'border-critical': { + value: 'rgba(253, 87, 73, 1)', + description: + 'For use as a border on critical components such as an outline on interactive elements in an error state.', + }, + 'border-critical-subdued': { + value: 'rgba(224, 179, 178, 1)', + description: 'For use as a border on critical components such as banners.', + }, + 'border-critical-disabled': { + value: 'rgba(255, 167, 163, 1)', + description: + 'For use as a disabled border on critical components such as banners, and as an outline on interactive elements in an error state.', + }, + 'icon-critical': { + value: 'rgba(215, 44, 13, 1)', + description: 'For use as an icon fill color on top of critical elements.', + }, + 'surface-critical': { + value: 'rgba(254, 211, 209, 1)', + description: + 'For use as a surface color on critical elements including badges.', + }, + 'surface-critical-subdued': { + value: 'rgba(255, 244, 244, 1)', + description: + 'For use as a subdued surface color on critical elements including banners.', + }, + 'surface-critical-subdued-hovered': { + value: 'rgba(255, 240, 240, 1)', + description: + 'For use as a surface color on critical interactive elements including action list items in a hovered state.', + }, + 'surface-critical-subdued-pressed': { + value: 'rgba(255, 233, 232, 1)', + description: + 'For use as a surface color on critical interactive elements including action list items in a pressed state.', + }, + 'surface-critical-subdued-depressed': { + value: 'rgba(254, 188, 185, 1)', + description: + 'For use as a surface color on critical interactive elements including action list items in a depressed state.', + }, + 'text-critical': { + value: 'rgba(215, 44, 13, 1)', + description: + 'For use as a text color in inert critical elements such as exception list. Not for use as a text color on banners and badges.', + }, + 'action-critical': { + value: 'rgba(216, 44, 13, 1)', + description: + 'For use as the background color for destructive buttons, and as the background color for error toast messages.', + }, + 'action-critical-disabled': { + value: 'rgba(241, 241, 241, 1)', + description: + 'For use as the background color for disabled destructive buttons, and as the background color for error toast messages.', + }, + 'action-critical-hovered': { + value: 'rgba(188, 34, 0, 1)', + description: + 'For use as the background color for hovered destructive buttons, and as the background color for error toast messages.', + }, + 'action-critical-pressed': { + value: 'rgba(162, 27, 0, 1)', + description: + 'For use as the background color for pressed destructive buttons, and as the background color for error toast messages.', + }, + 'action-critical-depressed': { + value: 'rgba(108, 15, 0, 1)', + description: + 'For use as the background color for depressed destructive buttons, and as the background color for error toast messages.', + }, + 'icon-on-critical': { + value: 'rgba(255, 255, 255, 1)', + description: 'For use as a fill color for icons on critical actions.', + }, + 'text-on-critical': { + value: 'rgba(255, 255, 255, 1)', + description: 'For use as a text color on critical actions.', + }, + 'interactive-critical': { + value: 'rgba(216, 44, 13, 1)', + description: + 'For use as the text color for destructive interactive elements: links, plain buttons, error state of selected checkboxes and radio buttons, as well as a text color on destructive action list items. Not for use on critical banners and badges.', + }, + 'interactive-critical-disabled': { + value: 'rgba(253, 147, 141, 1)', + description: + 'For use as a text color in disabled destructive plain buttons, as well as a text color on destructive action list items. Not for use on critical banners and badges.', + }, + 'interactive-critical-hovered': { + value: 'rgba(205, 41, 12, 1)', + description: + 'For use as a text color in hovered destructive plain buttons, as well as a text color on destructive action list items. Not for use on critical banners and badges.', + }, + 'interactive-critical-pressed': { + value: 'rgba(103, 15, 3, 1)', + description: + 'For use as a text color in pressed destructive plain buttons, as well as a text color on destructive action list items. Not for use on critical banners and badges.', + }, + 'border-warning': { + value: 'rgba(185, 137, 0, 1)', + description: 'For use as a border on warning components such as...', + }, + 'border-warning-subdued': { + value: 'rgba(225, 184, 120, 1)', + description: 'For use as a border on warning components such as banners.', + }, + 'icon-warning': { + value: 'rgba(185, 137, 0, 1)', + description: 'For use as an icon fill color on top of warning elements.', + }, + 'surface-warning': { + value: 'rgba(255, 215, 157, 1)', + description: + 'For use as a surface color on warning elements including badges.', + }, + 'surface-warning-subdued': { + value: 'rgba(255, 245, 234, 1)', + description: + 'For use as a subdued surface color on warning elements including banners.', + }, + 'surface-warning-subdued-hovered': { + value: 'rgba(255, 242, 226, 1)', + description: + 'For use as a subdued surface color on warning elements including banners.', + }, + 'surface-warning-subdued-pressed': { + value: 'rgba(255, 235, 211, 1)', + description: + 'For use as a subdued surface color on warning elements including banners.', + }, + 'text-warning': { + value: 'rgba(145, 106, 0, 1)', + description: + 'For use as a text color in inert critical elements such as exception list. Not for use as a text color on banners and badges.', + }, + 'border-highlight': { + value: 'rgba(68, 157, 167, 1)', + description: 'For use as a border on informational components such as...', + }, + 'border-highlight-subdued': { + value: 'rgba(152, 198, 205, 1)', + description: + 'For use as a border on informational components such as banners.', + }, + 'icon-highlight': { + value: 'rgba(0, 160, 172, 1)', + description: + 'For use as an icon fill color on top of informational elements.', + }, + 'surface-highlight': { + value: 'rgba(164, 232, 242, 1)', + description: + 'For use as a surface color on information elements including badges.', + }, + 'surface-highlight-subdued': { + value: 'rgba(235, 249, 252, 1)', + description: + 'For use as a surface color on information elements including banners.', + }, + 'surface-highlight-subdued-hovered': { + value: 'rgba(228, 247, 250, 1)', + description: + 'For use as a surface color on information elements including banners.', + }, + 'surface-highlight-subdued-pressed': { + value: 'rgba(213, 243, 248, 1)', + description: + 'For use as a surface color on information elements including banners.', + }, + 'text-highlight': { + value: 'rgba(52, 124, 132, 1)', + description: + 'For use as a text color in inert informational elements. Not for use as a text color on banners and badges.', + }, + 'border-success': { + value: 'rgba(0, 164, 124, 1)', + description: + 'For use as a border on success components such as text inputs.', + }, + 'border-success-subdued': { + value: 'rgba(149, 201, 180, 1)', + description: 'For use as a border on success components such as banners.', + }, + 'icon-success': { + value: 'rgba(0, 127, 95, 1)', + description: 'For use as an icon fill color on top of success elements.', + }, + 'surface-success': { + value: 'rgba(174, 233, 209, 1)', + description: + 'For use as a surface color on success elements including badges.', + }, + 'surface-success-subdued': { + value: 'rgba(241, 248, 245, 1)', + description: + 'For use as a surface color on information elements including banners.', + }, + 'surface-success-subdued-hovered': { + value: 'rgba(236, 246, 241, 1)', + description: + 'For use as a surface color on information elements including banners.', + }, + 'surface-success-subdued-pressed': { + value: 'rgba(226, 241, 234, 1)', + description: + 'For use as a surface color on information elements including banners.', + }, + 'text-success': { + value: 'rgba(0, 128, 96, 1)', + description: + 'For use as a text color in inert success elements. Not for use as a text color on banners and badges.', + }, + 'icon-attention': { + value: 'rgba(138, 97, 22, 1)', + }, + 'surface-attention': { + value: 'rgba(255, 234, 138, 1)', + }, + 'decorative-one-icon': { + value: 'rgba(126, 87, 0, 1)', + description: + 'For use as a decorative icon color that is applied on a decorative surface.', + }, + 'decorative-one-surface': { + value: 'rgba(255, 201, 107, 1)', + description: 'For use as a decorative surface color.', + }, + 'decorative-one-text': { + value: 'rgba(61, 40, 0, 1)', + description: + 'For use as a decorative text color that is applied on a decorative surface.', + }, + 'decorative-two-icon': { + value: 'rgba(175, 41, 78, 1)', + description: + 'For use as a decorative icon color that is applied on a decorative surface.', + }, + 'decorative-two-surface': { + value: 'rgba(255, 196, 176, 1)', + description: 'For use as a decorative surface color.', + }, + 'decorative-two-text': { + value: 'rgba(73, 11, 28, 1)', + description: + 'For use as a decorative text color that is applied on a decorative surface.', + }, + 'decorative-three-icon': { + value: 'rgba(0, 109, 65, 1)', + description: + 'For use as a decorative icon color that is applied on a decorative surface.', + }, + 'decorative-three-surface': { + value: 'rgba(146, 230, 181, 1)', + description: 'For use as a decorative surface color.', + }, + 'decorative-three-text': { + value: 'rgba(0, 47, 25, 1)', + description: + 'For use as a decorative text color that is applied on a decorative surface.', + }, + 'decorative-four-icon': { + value: 'rgba(0, 106, 104, 1)', + description: + 'For use as a decorative icon color that is applied on a decorative surface.', + }, + 'decorative-four-surface': { + value: 'rgba(145, 224, 214, 1)', + description: 'For use as a decorative surface color.', + }, + 'decorative-four-text': { + value: 'rgba(0, 45, 45, 1)', + description: + 'For use as a decorative text color that is applied on a decorative surface.', + }, + 'decorative-five-icon': { + value: 'rgba(174, 43, 76, 1)', + description: + 'For use as a decorative icon color that is applied on a decorative surface.', + }, + 'decorative-five-surface': { + value: 'rgba(253, 201, 208, 1)', + description: 'For use as a decorative surface color.', + }, + 'decorative-five-text': { + value: 'rgba(79, 14, 31, 1)', + description: + 'For use as a decorative text color that is applied on a decorative surface.', + }, +}; diff --git a/polaris-migrator/src/migrations/v9-scss-replace-color/v9-scss-replace-color.ts b/polaris-migrator/src/migrations/v9-scss-replace-color/v9-scss-replace-color.ts index afdd6fda4c6..02ddcf003f0 100644 --- a/polaris-migrator/src/migrations/v9-scss-replace-color/v9-scss-replace-color.ts +++ b/polaris-migrator/src/migrations/v9-scss-replace-color/v9-scss-replace-color.ts @@ -2,7 +2,7 @@ import type {FileInfo, API, Options} from 'jscodeshift'; import postcss from 'postcss'; import type {Plugin} from 'postcss'; import valueParser from 'postcss-value-parser'; -import {colors as tokenColors, createVar} from '@shopify/polaris-tokens'; +import {createVar} from '@shopify/polaris-tokens'; import { namespace, @@ -13,6 +13,8 @@ import { import type {NamespaceOptions} from '../../utilities/sass'; import {isKeyOf} from '../../utilities/type-guards'; +import {colors as tokenColors} from './v10-legacy-colors'; + export default function scssReplaceColor( file: FileInfo, _: API, diff --git a/polaris-react/package.json b/polaris-react/package.json index 5a589942142..98c4de0a114 100644 --- a/polaris-react/package.json +++ b/polaris-react/package.json @@ -10,6 +10,9 @@ "bugs": { "url": "https://github.com/Shopify/polaris/issues" }, + "engines": { + "node": "^16.17.0 || >=18.12.0" + }, "publishConfig": { "access": "public", "@shopify:registry": "https://registry.npmjs.org" @@ -23,8 +26,7 @@ ], "sideEffects": [ "**/*.css", - "**/*.scss", - "**/configure.{js,mjs,esnext,ts}" + "**/*.scss" ], "keywords": [ "shopify", @@ -36,18 +38,10 @@ "main": "build/cjs/index.js", "module": "build/esm/index.js", "esnext": "build/esnext/index.esnext", - "types": "build/ts/latest/src/index.d.ts", - "typesVersions": { - "<4.0": { - "build/types/latest/*": [ - "build/ts/3.4/*" - ] - } - }, + "types": "build/ts/src/index.d.ts", "scripts": { - "build": "run-s build:types build:types-downlevel build:js build:validate", + "build": "run-s build:types build:js build:validate", "build:types": "tsc -b", - "build:types-downlevel": "downlevel-dts './build/ts/latest' './build/ts/3.4'", "build:js": "rollup -c", "build:validate": "node scripts/build-validate.js", "dev": "yarn run storybook", @@ -71,14 +65,11 @@ "react-transition-group": "^4.4.2" }, "peerDependencies": { - "react": "^16.14.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0" + "react": "^18.0.0", + "react-dom": "^18.0.0" }, "devDependencies": { - "@babel/core": "^7.15.0", - "@babel/node": "^7.14.9", "@changesets/get-release-plan": "^3.0.13", - "@shopify/browserslist-config": "^3.0.0", "@shopify/jest-dom-mocks": "^3.0.5", "@shopify/postcss-plugin": "^5.0.1", "@shopify/react-testing": "^4.1.0", @@ -89,11 +80,8 @@ "@storybook/builder-webpack5": "^6.5.12", "@storybook/manager-webpack5": "^6.5.12", "@storybook/react": "^6.5.12", - "@types/node": "^16.11.11", "@types/react": "^18.0.15", "@types/react-dom": "^18.0.6", - "babel-core": "7.0.0-bridge.0", - "babel-loader": "^8.1.0", "change-case": "^3.1.0", "chromatic": "^6.5.4", "create-file-webpack": "^1.0.2", diff --git a/polaris-react/playground/DetailsPage.tsx b/polaris-react/playground/DetailsPage.tsx index e13a7459eb3..b33ba8c39b9 100644 --- a/polaris-react/playground/DetailsPage.tsx +++ b/polaris-react/playground/DetailsPage.tsx @@ -528,8 +528,7 @@ export function DetailsPage() { alt={file.name} source={ validImageTypes.indexOf(file.type) > 0 - ? // eslint-disable-next-line node/no-unsupported-features/node-builtins - URL.createObjectURL(file) + ? URL.createObjectURL(file) : 'https://cdn.shopify.com/s/files/1/0757/9955/files/New_Post.png?12678548500147524304' } /> @@ -548,7 +547,7 @@ export function DetailsPage() { const actualPageMarkup = ( Success badge} primaryAction={{ diff --git a/polaris-react/rollup.config.mjs b/polaris-react/rollup.config.mjs index 34ca161cb6f..150534584fd 100644 --- a/polaris-react/rollup.config.mjs +++ b/polaris-react/rollup.config.mjs @@ -33,7 +33,6 @@ function generateConfig({output, targets, stylesConfig}) { // Options that may be present on the `babelConfig` object but // we want to override envName: 'production', - // @ts-expect-error targets is a valid babel option but @types/babel__core doesn't know that yet targets, }), replace({ @@ -54,7 +53,7 @@ function generateConfig({output, targets, stylesConfig}) { /** @type {import('rollup').RollupOptions} */ export default [ generateConfig({ - targets: 'extends @shopify/browserslist-config, node 12.20', + targets: [...pkg.browserslist, 'node 16.17.0'], stylesConfig: { mode: 'standalone', output: 'styles.css', @@ -69,14 +68,14 @@ export default [ format: 'cjs', dir: path.dirname(pkg.main), preserveModules: true, - entryFileNames: '[name][assetExtname].js', + entryFileNames: '[name].js', exports: 'named', }, { format: 'esm', dir: path.dirname(pkg.module), preserveModules: true, - entryFileNames: '[name][assetExtname].js', + entryFileNames: '[name].js', }, ], }), @@ -95,7 +94,7 @@ export default [ format: 'esm', dir: path.dirname(pkg.esnext), preserveModules: true, - entryFileNames: '[name][assetExtname].esnext', + entryFileNames: '[name].esnext', }, ], }), diff --git a/polaris-react/scripts/build-validate.js b/polaris-react/scripts/build-validate.js index 568d0ae50b0..8a080a6cb2a 100644 --- a/polaris-react/scripts/build-validate.js +++ b/polaris-react/scripts/build-validate.js @@ -1,4 +1,3 @@ -// eslint-disable-next-line node/no-unsupported-features/node-builtins const assert = require('assert').strict; const fs = require('fs'); @@ -44,7 +43,7 @@ function validateStandardBuild() { const cssContent = fs.readFileSync('./build/esm/styles.css', 'utf-8'); assert.ok(cssContent.includes('.Polaris-Avatar {')); assert.ok(cssContent.includes('.Polaris-BulkActions__BulkActionButton {')); - assert.ok(cssContent.includes('@keyframes p-keyframes-bounce {')); + assert.ok(cssContent.includes('@keyframes p-motion-keyframes-bounce {')); assert.ok( cssContent.includes( '--p-motion-keyframes-bounce:p-motion-keyframes-bounce;', @@ -71,7 +70,9 @@ function validateEsNextBuild() { 'utf-8', ); assert.ok(cssContent.includes('.Polaris-Avatar_z763p {')); - assert.ok(cssKeyframesContent.includes('@keyframes p-keyframes-spin {')); + assert.ok( + cssKeyframesContent.includes('@keyframes p-motion-keyframes-spin {'), + ); assert.ok( cssKeyframesContent.includes( '--p-motion-keyframes-spin:p-motion-keyframes-spin;', @@ -89,9 +90,7 @@ function validateEsNextBuild() { } function validateAncillaryOutput() { - assert.ok(fs.existsSync('./build/ts/latest/src/index.d.ts')); - // Downleveled for consumers on older TypeScript versions - assert.ok(fs.existsSync('./build/ts/3.4/src/index.d.ts')); + assert.ok(fs.existsSync('./build/ts/src/index.d.ts')); } function validateVersionReplacement() { @@ -119,10 +118,7 @@ function validateVersionReplacement() { assert.strictEqual(fileBuckets.includesTemplateString.length, 0); assert.deepStrictEqual(fileBuckets.includesVersion, [ - './build/cjs/configure.js', - './build/esm/configure.js', './build/esm/styles.css', - './build/esnext/configure.esnext', './build/esnext/components/AppProvider/AppProvider.css', ]); } diff --git a/polaris-react/src/components/AccountConnection/AccountConnection.tsx b/polaris-react/src/components/AccountConnection/AccountConnection.tsx index 6d0e5934215..c6f066d23ab 100644 --- a/polaris-react/src/components/AccountConnection/AccountConnection.tsx +++ b/polaris-react/src/components/AccountConnection/AccountConnection.tsx @@ -4,7 +4,7 @@ import type {Action} from '../../types'; import {Avatar} from '../Avatar'; import {buttonFrom} from '../Button'; import {SettingAction} from '../SettingAction'; -import {AlphaCard} from '../AlphaCard'; +import {Card} from '../Card'; import {Box} from '../Box'; import {HorizontalStack} from '../HorizontalStack'; import {Text} from '../Text'; @@ -69,7 +69,7 @@ export function AccountConnection({ : null; return ( - + {avatarMarkup} @@ -80,6 +80,6 @@ export function AccountConnection({ {termsOfServiceMarkup} - +
); } diff --git a/polaris-react/src/components/ActionList/components/Item/Item.tsx b/polaris-react/src/components/ActionList/components/Item/Item.tsx index 5ce60563ae0..87c97a97bda 100644 --- a/polaris-react/src/components/ActionList/components/Item/Item.tsx +++ b/polaris-react/src/components/ActionList/components/Item/Item.tsx @@ -164,7 +164,7 @@ export const TruncateText = ({children}: {children: string}) => { return isOverflowing ? ( ; - -export function Default() { - return ( - - - - Online store dashboard - -

View a summary of your online store’s performance.

-
-
- ); -} - -export function WithBackgroundSubdued() { - return ( - - - - Online store dashboard - -

View a summary of your online store’s performance.

-
-
- ); -} - -export function WithBorderRadiusRoundedAbove() { - return ( - - - - Online store dashboard - -

View a summary of your online store’s performance.

-
-
- ); -} - -export function WithResponsivePadding() { - return ( - - - - Online store dashboard - -

View a summary of your online store’s performance.

-
-
- ); -} - -export function WithSubduedSection() { - return ( - - - - Staff accounts - - - - Felix Crafford - Ezequiel Manno - - - - - - - - - Deactivated staff accounts - - - Felix Crafford - Ezequiel Manno - - - - - - ); -} diff --git a/polaris-react/src/components/AlphaCard/AlphaCard.tsx b/polaris-react/src/components/AlphaCard/AlphaCard.tsx deleted file mode 100644 index 26910c264a9..00000000000 --- a/polaris-react/src/components/AlphaCard/AlphaCard.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import type { - BreakpointsAlias, - ColorBackgroundAlias, - BorderRadiusScale, - SpaceScale, -} from '@shopify/polaris-tokens'; -import React from 'react'; - -import {useBreakpoints} from '../../utilities/breakpoints'; -import type {ResponsiveProp} from '../../utilities/css'; -import {Box} from '../Box'; - -type Spacing = ResponsiveProp; - -export interface AlphaCardProps { - children?: React.ReactNode; - /** Background color - * @default 'bg' - */ - background?: ColorBackgroundAlias; - /** The spacing around the card - * @default {xs: '4', sm: '5'} - * @example - * padding='4' - * padding={{xs: '2', sm: '3', md: '4', lg: '5', xl: '6'}} - */ - padding?: Spacing; - /** Border radius value above a set breakpoint */ - roundedAbove?: BreakpointsAlias; -} - -export const AlphaCard = ({ - children, - background = 'bg', - padding = {xs: '4', sm: '5'}, - roundedAbove, -}: AlphaCardProps) => { - const breakpoints = useBreakpoints(); - const defaultBorderRadius: BorderRadiusScale = '2'; - - let hasBorderRadius = !roundedAbove; - - if (roundedAbove && breakpoints[`${roundedAbove}Up`]) { - hasBorderRadius = true; - } - - return ( - - {children} - - ); -}; diff --git a/polaris-react/src/components/AlphaCard/index.ts b/polaris-react/src/components/AlphaCard/index.ts deleted file mode 100644 index 48ec946eecf..00000000000 --- a/polaris-react/src/components/AlphaCard/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './AlphaCard'; diff --git a/polaris-react/src/components/AlphaCard/tests/AlphaCard.test.tsx b/polaris-react/src/components/AlphaCard/tests/AlphaCard.test.tsx deleted file mode 100644 index 8048d406b26..00000000000 --- a/polaris-react/src/components/AlphaCard/tests/AlphaCard.test.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react'; -import {mountWithApp} from 'tests/utilities'; -import {matchMedia} from '@shopify/jest-dom-mocks'; -import {setMediaWidth} from 'tests/utilities/breakpoints'; - -import {AlphaCard} from '..'; - -const heading =

Online store dashboard

; -const subheading =

View a summary of your online store performance

; - -describe('AlphaCard', () => { - beforeEach(() => { - matchMedia.mock(); - }); - - afterEach(() => { - matchMedia.restore(); - }); - - it('renders children', () => { - const alphaCard = mountWithApp( - - {heading} - {subheading} - , - ); - - expect(alphaCard).toContainReactComponentTimes('p', 2); - }); - - it('sets default border radius when roundedAbove breakpoint passed in', () => { - setMediaWidth('breakpoints-sm'); - const alphaCard = mountWithApp( - - {heading} - {subheading} - , - ); - - expect(alphaCard).toContainReactComponent('div', { - style: expect.objectContaining({ - '--pc-box-border-radius': 'var(--p-border-radius-2)', - }), - }); - }); -}); diff --git a/polaris-react/src/components/AlphaFilters/AlphaFilters.scss b/polaris-react/src/components/AlphaFilters/AlphaFilters.scss deleted file mode 100644 index f5d0a3eb27e..00000000000 --- a/polaris-react/src/components/AlphaFilters/AlphaFilters.scss +++ /dev/null @@ -1,235 +0,0 @@ -@import '../../styles/common'; - -.Container { - position: relative; - z-index: var(--p-z-index-1); - border-bottom: var(--p-border-width-1) solid var(--p-color-border-subdued); - border-top-left-radius: var(--p-border-radius-2); - border-top-right-radius: var(--p-border-radius-2); - background: var(--p-color-bg); -} - -@media #{$p-breakpoints-sm-down} { - .Container { - border-top-left-radius: 0; - border-top-right-radius: 0; - height: 57px; - } -} - -.SearchField { - flex: 1; -} - -.Spinner { - width: var(--p-space-5); - transform: translateX(var(--p-space-1)); - - svg { - display: block; - } -} - -.FiltersWrapper { - border-bottom: var(--p-border-width-1) solid var(--p-color-border-subdued); - height: 53px; - overflow: hidden; - - @media #{$p-breakpoints-sm-down} { - background: var(--p-color-bg); - } - - @media #{$p-breakpoints-md-up} { - height: auto; - overflow: visible; - } -} - -.hideQueryField .FiltersWrapper { - display: flex; - align-items: center; -} - -.FiltersInner { - overflow: auto; - white-space: nowrap; - padding: var(--p-space-3) var(--p-space-4) var(--p-space-5); -} - -.hideQueryField .FiltersInner { - flex: 1; -} - -@media #{$p-breakpoints-md-up} { - .FiltersInner { - overflow: visible; - flex-wrap: wrap; - gap: var(--p-space-2); - padding: calc(var(--p-space-2) - var(--p-space-05)) var(--p-space-2); - } -} - -.AddFilter { - background: var(--p-color-bg-subdued); - border-radius: var(--p-border-radius-6); - border: var(--p-color-border-subdued) dashed var(--p-border-width-1); - padding: 0 var(--p-space-2) 0 var(--p-space-3); - height: 28px; - cursor: pointer; - color: var(--p-color-text); - display: flex; - align-items: center; - justify-content: center; - font-size: var(--p-font-size-100); - font-family: var(--p-font-family-sans); - line-height: var(--p-font-line-height-2); - outline: inherit; - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - @include focus-ring($border-width: var(--p-border-width-1)); - - path { - fill: var(--p-color-icon); - } - - @media #{$p-breakpoints-md-up} { - font-size: var(--p-font-size-75); - line-height: var(--p-font-line-height-1); - height: 24px; - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - padding: 0 6px 0 var(--p-space-2); - } - - &:hover, - &:focus { - background: var(--p-color-bg-hover); - border-color: var(--p-color-border-hover); - - path { - fill: var(--p-color-icon-hover); - } - } - - &:active { - background: var(--p-color-bg-active); - border-color: var(--p-color-border-hover); - } - - &[aria-disabled='true'] { - background: var(--p-color-bg-disabled); - border-color: var(--p-color-border-disabled); - color: var(--p-color-text-disabled); - cursor: default; - - path { - fill: var(--p-color-icon-disabled); - } - } - - &:focus-visible:not(:active) { - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - @include focus-ring($style: 'focused'); - } - - &::after { - border-radius: var(--p-border-radius-6); - } - - span { - margin-right: var(--p-space-05); - - @media #{$p-breakpoints-md-up} { - margin-right: var(--p-space-025); - } - } - - svg { - width: var(--p-space-5); - - @media #{$p-breakpoints-md-up} { - width: var(--p-space-4); - } - } -} - -@media #{$p-breakpoints-md-down} { - .FiltersWrapperWithAddButton { - position: relative; - - .FiltersInner { - padding-top: var(--p-space-2); - padding-bottom: var(--p-space-5); - padding-right: 0; - } - } - - .AddFilterActivatorMultiple { - position: sticky; - z-index: var(--p-z-index-1); - top: 0; - right: 0; - display: flex; - padding: var(--p-space-1) var(--p-space-4) var(--p-space-1) 0; - background: var(--p-color-bg); - margin-left: var(--p-space-2); - - &::before { - content: ''; - position: absolute; - top: 0; - left: -12px; - width: 12px; - height: 100%; - pointer-events: none; - // stylelint-disable -- generated by polaris-migrator DO NOT COPY - background: linear-gradient( - 90deg, - rgba(255, 255, 255, 0) 0%, - var(--p-color-bg) 70%, - var(--p-color-bg) 100% - ); - // stylelint-enable - } - - .AddFilter { - // stylelint-disable-next-line -- hardcoded value required for responsive design - padding: 0.75rem var(--p-space-2); - - // stylelint-disable-next-line selector-max-combinators -- generated by polaris-migrator DO NOT COPY - span { - display: none; - } - } - } -} - -.FiltersStickyArea { - position: relative; - display: flex; - gap: var(--p-space-1); - flex-wrap: nowrap; - align-items: center; - justify-content: flex-start; - - @media #{$p-breakpoints-md-up} { - flex-wrap: wrap; - } -} - -.ClearAll { - margin-left: var(--p-space-1); -} - -@media #{$p-breakpoints-md-down} { - .ClearAll { - margin-left: var(--p-space-2); - padding-right: var(--p-space-4); - } - - .MultiplePinnedFilterClearAll { - transform: translateX(-8px); - position: relative; - z-index: var(--p-z-index-1); - margin-left: 0; - padding-right: var(--p-space-4); - } -} diff --git a/polaris-react/src/components/AlphaFilters/AlphaFilters.stories.tsx b/polaris-react/src/components/AlphaFilters/AlphaFilters.stories.tsx deleted file mode 100644 index 1afda8107f8..00000000000 --- a/polaris-react/src/components/AlphaFilters/AlphaFilters.stories.tsx +++ /dev/null @@ -1,1453 +0,0 @@ -import React, {useCallback, useState} from 'react'; -import type {ComponentMeta} from '@storybook/react'; -import { - Avatar, - Button, - LegacyCard, - ChoiceList, - DataTable, - AlphaFilters, - RangeSlider, - ResourceList, - TextField, - Text, -} from '@shopify/polaris'; - -export default { - component: AlphaFilters, - parameters: { - a11y: { - config: { - // disabled due to DataTable having a scrollable region without - // keyboard access when all content fits without scrolling. - rules: [{id: 'scrollable-region-focusable', enabled: false}], - }, - }, - }, -} as ComponentMeta; - -export function WithAResourceList() { - const [accountStatus, setAccountStatus] = useState(null); - const [moneySpent, setMoneySpent] = useState(null); - const [taggedWith, setTaggedWith] = useState(null); - const [queryValue, setQueryValue] = useState(null); - - const handleAccountStatusChange = useCallback( - (value) => setAccountStatus(value), - [], - ); - const handleMoneySpentChange = useCallback( - (value) => setMoneySpent(value), - [], - ); - const handleTaggedWithChange = useCallback( - (value) => setTaggedWith(value), - [], - ); - const handleFiltersQueryChange = useCallback( - (value) => setQueryValue(value), - [], - ); - const handleAccountStatusRemove = useCallback( - () => setAccountStatus(null), - [], - ); - const handleMoneySpentRemove = useCallback(() => setMoneySpent(null), []); - const handleTaggedWithRemove = useCallback(() => setTaggedWith(null), []); - const handleQueryValueRemove = useCallback(() => setQueryValue(null), []); - const handleFiltersClearAll = useCallback(() => { - handleAccountStatusRemove(); - handleMoneySpentRemove(); - handleTaggedWithRemove(); - handleQueryValueRemove(); - }, [ - handleAccountStatusRemove, - handleMoneySpentRemove, - handleQueryValueRemove, - handleTaggedWithRemove, - ]); - - const filters = [ - { - key: 'accountStatus', - label: 'Account status', - filter: ( - - ), - shortcut: true, - pinned: true, - }, - { - key: 'taggedWith', - label: 'Tagged with', - filter: ( - - ), - shortcut: true, - pinned: true, - }, - { - key: 'moneySpent', - label: 'Money spent', - filter: ( - - ), - }, - ]; - - const appliedFilters = []; - if (!isEmpty(accountStatus)) { - const key = 'accountStatus'; - appliedFilters.push({ - key, - label: disambiguateLabel(key, accountStatus), - onRemove: handleAccountStatusRemove, - }); - } - if (!isEmpty(moneySpent)) { - const key = 'moneySpent'; - appliedFilters.push({ - key, - label: disambiguateLabel(key, moneySpent), - onRemove: handleMoneySpentRemove, - }); - } - if (!isEmpty(taggedWith)) { - const key = 'taggedWith'; - appliedFilters.push({ - key, - label: disambiguateLabel(key, taggedWith), - onRemove: handleTaggedWithRemove, - }); - } - - return ( -
- - - } - flushFilters - items={[ - { - id: 341, - url: '#', - name: 'Mae Jemison', - location: 'Decatur, USA', - }, - { - id: 256, - url: '#', - name: 'Ellen Ochoa', - location: 'Los Angeles, USA', - }, - ]} - renderItem={(item) => { - const {id, url, name, location} = item; - const media = ; - - return ( - - - {name} - -
{location}
-
- ); - }} - /> -
-
- ); - - function disambiguateLabel(key, value) { - switch (key) { - case 'moneySpent': - return `Money spent is between $${value[0]} and $${value[1]}`; - case 'taggedWith': - return `Tagged with ${value}`; - case 'accountStatus': - return value.map((val) => `Customer ${val}`).join(', '); - default: - return value; - } - } - - function isEmpty(value) { - if (Array.isArray(value)) { - return value.length === 0; - } else { - return value === '' || value == null; - } - } -} - -export function WithADataTable() { - const [availability, setAvailability] = useState(null); - const [productType, setProductType] = useState(null); - const [taggedWith, setTaggedWith] = useState(null); - const [queryValue, setQueryValue] = useState(null); - - const handleAvailabilityChange = useCallback( - (value) => setAvailability(value), - [], - ); - const handleProductTypeChange = useCallback( - (value) => setProductType(value), - [], - ); - const handleTaggedWithChange = useCallback( - (value) => setTaggedWith(value), - [], - ); - const handleFiltersQueryChange = useCallback( - (value) => setQueryValue(value), - [], - ); - const handleAvailabilityRemove = useCallback(() => setAvailability(null), []); - const handleProductTypeRemove = useCallback(() => setProductType(null), []); - const handleTaggedWithRemove = useCallback(() => setTaggedWith(null), []); - const handleQueryValueRemove = useCallback(() => setQueryValue(null), []); - const handleFiltersClearAll = useCallback(() => { - handleAvailabilityRemove(); - handleProductTypeRemove(); - handleTaggedWithRemove(); - handleQueryValueRemove(); - }, [ - handleAvailabilityRemove, - handleQueryValueRemove, - handleProductTypeRemove, - handleTaggedWithRemove, - ]); - - const filters = [ - { - key: 'availability', - label: 'Availability', - filter: ( - - ), - shortcut: true, - }, - { - key: 'productType', - label: 'Product type', - filter: ( - - ), - }, - { - key: 'taggedWith', - label: 'Tagged with', - filter: ( - - ), - }, - ]; - - const appliedFilters = []; - if (!isEmpty(availability)) { - const key = 'availability'; - appliedFilters.push({ - key, - label: disambiguateLabel(key, availability), - onRemove: handleAvailabilityRemove, - }); - } - if (!isEmpty(productType)) { - const key = 'productType'; - appliedFilters.push({ - key, - label: disambiguateLabel(key, productType), - onRemove: handleProductTypeRemove, - }); - } - if (!isEmpty(taggedWith)) { - const key = 'taggedWith'; - appliedFilters.push({ - key, - label: disambiguateLabel(key, taggedWith), - onRemove: handleTaggedWithRemove, - }); - } - - return ( -
- - - - -
- ); - - function disambiguateLabel(key, value) { - switch (key) { - case 'taggedWith': - return `Tagged with ${value}`; - case 'availability': - return value.map((val) => `Available on ${val}`).join(', '); - case 'productType': - return value.join(', '); - default: - return value; - } - } - - function isEmpty(value) { - if (Array.isArray(value)) { - return value.length === 0; - } else { - return value === '' || value == null; - } - } -} - -export function WithChildrenContent() { - const [taggedWith, setTaggedWith] = useState(null); - const [queryValue, setQueryValue] = useState(null); - - const handleTaggedWithChange = useCallback( - (value) => setTaggedWith(value), - [], - ); - const handleQueryValueChange = useCallback( - (value) => setQueryValue(value), - [], - ); - const handleTaggedWithRemove = useCallback(() => setTaggedWith(null), []); - const handleQueryValueRemove = useCallback(() => setQueryValue(null), []); - - const handleClearAll = useCallback(() => { - handleTaggedWithRemove(); - handleQueryValueRemove(); - }, [handleQueryValueRemove, handleTaggedWithRemove]); - - const filters = [ - { - key: 'taggedWith', - label: 'Tagged with', - filter: ( - - ), - shortcut: true, - }, - ]; - - const appliedFilters = !isEmpty(taggedWith) - ? [ - { - key: 'taggedWith', - label: disambiguateLabel('taggedWith', taggedWith), - onRemove: handleTaggedWithRemove, - }, - ] - : []; - - return ( -
- - -
- -
- - } - flushFilters - items={[ - { - id: 341, - url: '#', - name: 'Mae Jemison', - location: 'Decatur, USA', - }, - { - id: 256, - url: '#', - name: 'Ellen Ochoa', - location: 'Los Angeles, USA', - }, - ]} - renderItem={(item) => { - const {id, url, name, location} = item; - const media = ; - - return ( - - - {name} - -
{location}
-
- ); - }} - /> -
-
- ); - - function disambiguateLabel(key, value) { - switch (key) { - case 'taggedWith': - return `Tagged with ${value}`; - default: - return value; - } - } - - function isEmpty(value) { - if (Array.isArray(value)) { - return value.length === 0; - } else { - return value === '' || value == null; - } - } -} - -export function Disabled() { - const [taggedWith, setTaggedWith] = useState(null); - const [queryValue, setQueryValue] = useState(null); - - const handleTaggedWithChange = useCallback( - (value) => setTaggedWith(value), - [], - ); - const handleQueryValueChange = useCallback( - (value) => setQueryValue(value), - [], - ); - const handleTaggedWithRemove = useCallback(() => setTaggedWith(null), []); - const handleQueryValueRemove = useCallback(() => setQueryValue(null), []); - - const handleClearAll = useCallback(() => { - handleTaggedWithRemove(); - handleQueryValueRemove(); - }, [handleQueryValueRemove, handleTaggedWithRemove]); - - const filters = [ - { - key: 'taggedWith', - label: 'Tagged with', - filter: ( - - ), - shortcut: true, - }, - ]; - - const appliedFilters = !isEmpty(taggedWith) - ? [ - { - key: 'taggedWith', - label: disambiguateLabel('taggedWith', taggedWith), - onRemove: handleTaggedWithRemove, - }, - ] - : []; - - return ( -
- - -
- -
- - } - flushFilters - items={[ - { - id: 341, - url: '#', - name: 'Mae Jemison', - location: 'Decatur, USA', - }, - { - id: 256, - url: '#', - name: 'Ellen Ochoa', - location: 'Los Angeles, USA', - }, - ]} - renderItem={(item) => { - const {id, url, name, location} = item; - const media = ; - - return ( - - - {name} - -
{location}
-
- ); - }} - /> -
-
- ); - - function disambiguateLabel(key, value) { - switch (key) { - case 'taggedWith': - return `Tagged with ${value}`; - default: - return value; - } - } - - function isEmpty(value) { - if (Array.isArray(value)) { - return value.length === 0; - } else { - return value === '' || value == null; - } - } -} - -export function SomeDisabled() { - const [taggedWith, setTaggedWith] = useState(null); - const [vendor, setVendor] = useState(null); - const [queryValue, setQueryValue] = useState(null); - - const handleTaggedWithChange = useCallback( - (value) => setTaggedWith(value), - [], - ); - const handleQueryValueChange = useCallback( - (value) => setQueryValue(value), - [], - ); - const handleVendorChange = useCallback((value) => setVendor(value), []); - - const handleTaggedWithRemove = useCallback(() => setTaggedWith(null), []); - const handleQueryValueRemove = useCallback(() => setQueryValue(null), []); - const handleVendorRemove = useCallback(() => setVendor(null), []); - - const handleClearAll = useCallback(() => { - handleTaggedWithRemove(); - handleQueryValueRemove(); - handleVendorRemove(); - }, [handleQueryValueRemove, handleTaggedWithRemove, handleVendorRemove]); - - const filters = [ - { - key: 'taggedWith', - label: 'Tagged with', - filter: ( - - ), - shortcut: true, - }, - { - key: 'vendor', - label: 'Vendor', - filter: ( - - ), - shortcut: true, - disabled: true, - }, - ]; - - const appliedFilters = !isEmpty(taggedWith) - ? [ - { - key: 'taggedWith', - label: disambiguateLabel('taggedWith', taggedWith), - onRemove: handleTaggedWithRemove, - }, - ] - : []; - - return ( -
- - -
- -
- - } - flushFilters - items={[ - { - id: 341, - url: '#', - name: 'Mae Jemison', - location: 'Decatur, USA', - }, - { - id: 256, - url: '#', - name: 'Ellen Ochoa', - location: 'Los Angeles, USA', - }, - ]} - renderItem={(item) => { - const {id, url, name, location} = item; - const media = ; - - return ( - - - {name} - -
{location}
-
- ); - }} - /> -
-
- ); - - function disambiguateLabel(key, value) { - switch (key) { - case 'taggedWith': - return `Tagged with ${value}`; - default: - return value; - } - } - - function isEmpty(value) { - if (Array.isArray(value)) { - return value.length === 0; - } else { - return value === '' || value == null; - } - } -} - -export function WithQueryFieldHidden() { - const [accountStatus, setAccountStatus] = useState(null); - const [moneySpent, setMoneySpent] = useState(null); - const [taggedWith, setTaggedWith] = useState(null); - const [queryValue, setQueryValue] = useState(null); - - const handleAccountStatusChange = useCallback( - (value) => setAccountStatus(value), - [], - ); - const handleMoneySpentChange = useCallback( - (value) => setMoneySpent(value), - [], - ); - const handleTaggedWithChange = useCallback( - (value) => setTaggedWith(value), - [], - ); - const handleFiltersQueryChange = useCallback( - (value) => setQueryValue(value), - [], - ); - const handleAccountStatusRemove = useCallback( - () => setAccountStatus(null), - [], - ); - const handleMoneySpentRemove = useCallback(() => setMoneySpent(null), []); - const handleTaggedWithRemove = useCallback(() => setTaggedWith(null), []); - const handleQueryValueRemove = useCallback(() => setQueryValue(null), []); - const handleFiltersClearAll = useCallback(() => { - handleAccountStatusRemove(); - handleMoneySpentRemove(); - handleTaggedWithRemove(); - handleQueryValueRemove(); - }, [ - handleAccountStatusRemove, - handleMoneySpentRemove, - handleQueryValueRemove, - handleTaggedWithRemove, - ]); - - const filters = [ - { - key: 'accountStatus', - label: 'Account status', - filter: ( - - ), - shortcut: true, - }, - { - key: 'taggedWith', - label: 'Tagged with', - filter: ( - - ), - shortcut: true, - }, - { - key: 'moneySpent', - label: 'Money spent', - filter: ( - - ), - }, - ]; - - const appliedFilters = []; - if (!isEmpty(accountStatus)) { - const key = 'accountStatus'; - appliedFilters.push({ - key, - label: disambiguateLabel(key, accountStatus), - onRemove: handleAccountStatusRemove, - }); - } - if (!isEmpty(moneySpent)) { - const key = 'moneySpent'; - appliedFilters.push({ - key, - label: disambiguateLabel(key, moneySpent), - onRemove: handleMoneySpentRemove, - }); - } - if (!isEmpty(taggedWith)) { - const key = 'taggedWith'; - appliedFilters.push({ - key, - label: disambiguateLabel(key, taggedWith), - onRemove: handleTaggedWithRemove, - }); - } - - return ( -
- - - - - } - flushFilters - items={[ - { - id: 341, - url: '#', - name: 'Mae Jemison', - location: 'Decatur, USA', - }, - { - id: 256, - url: '#', - name: 'Ellen Ochoa', - location: 'Los Angeles, USA', - }, - ]} - renderItem={(item) => { - const {id, url, name, location} = item; - const media = ; - - return ( - - - {name} - -
{location}
-
- ); - }} - /> -
-
- ); - - function disambiguateLabel(key, value) { - switch (key) { - case 'moneySpent': - return `Money spent is between $${value[0]} and $${value[1]}`; - case 'taggedWith': - return `Tagged with ${value}`; - case 'accountStatus': - return value.map((val) => `Customer ${val}`).join(', '); - default: - return value; - } - } - - function isEmpty(value) { - if (Array.isArray(value)) { - return value.length === 0; - } else { - return value === '' || value == null; - } - } -} - -export function WithQueryFieldDisabled() { - const [accountStatus, setAccountStatus] = useState(null); - const [moneySpent, setMoneySpent] = useState(null); - const [taggedWith, setTaggedWith] = useState(null); - const [queryValue, setQueryValue] = useState(null); - - const handleAccountStatusChange = useCallback( - (value) => setAccountStatus(value), - [], - ); - const handleMoneySpentChange = useCallback( - (value) => setMoneySpent(value), - [], - ); - const handleTaggedWithChange = useCallback( - (value) => setTaggedWith(value), - [], - ); - const handleFiltersQueryChange = useCallback( - (value) => setQueryValue(value), - [], - ); - const handleAccountStatusRemove = useCallback( - () => setAccountStatus(null), - [], - ); - const handleMoneySpentRemove = useCallback(() => setMoneySpent(null), []); - const handleTaggedWithRemove = useCallback(() => setTaggedWith(null), []); - const handleQueryValueRemove = useCallback(() => setQueryValue(null), []); - const handleFiltersClearAll = useCallback(() => { - handleAccountStatusRemove(); - handleMoneySpentRemove(); - handleTaggedWithRemove(); - handleQueryValueRemove(); - }, [ - handleAccountStatusRemove, - handleMoneySpentRemove, - handleQueryValueRemove, - handleTaggedWithRemove, - ]); - - const filters = [ - { - key: 'accountStatus', - label: 'Account status', - filter: ( - - ), - shortcut: true, - }, - { - key: 'taggedWith', - label: 'Tagged with', - filter: ( - - ), - shortcut: true, - }, - { - key: 'moneySpent', - label: 'Money spent', - filter: ( - - ), - }, - ]; - - const appliedFilters = []; - if (!isEmpty(accountStatus)) { - const key = 'accountStatus'; - appliedFilters.push({ - key, - label: disambiguateLabel(key, accountStatus), - onRemove: handleAccountStatusRemove, - }); - } - if (!isEmpty(moneySpent)) { - const key = 'moneySpent'; - appliedFilters.push({ - key, - label: disambiguateLabel(key, moneySpent), - onRemove: handleMoneySpentRemove, - }); - } - if (!isEmpty(taggedWith)) { - const key = 'taggedWith'; - appliedFilters.push({ - key, - label: disambiguateLabel(key, taggedWith), - onRemove: handleTaggedWithRemove, - }); - } - - return ( -
- - - } - flushFilters - items={[ - { - id: 341, - url: '#', - name: 'Mae Jemison', - location: 'Decatur, USA', - }, - { - id: 256, - url: '#', - name: 'Ellen Ochoa', - location: 'Los Angeles, USA', - }, - ]} - renderItem={(item) => { - const {id, url, name, location} = item; - const media = ; - - return ( - - - {name} - -
{location}
-
- ); - }} - /> -
-
- ); - - function disambiguateLabel(key, value) { - switch (key) { - case 'moneySpent': - return `Money spent is between $${value[0]} and $${value[1]}`; - case 'taggedWith': - return `Tagged with ${value}`; - case 'accountStatus': - return value.map((val) => `Customer ${val}`).join(', '); - default: - return value; - } - } - - function isEmpty(value) { - if (Array.isArray(value)) { - return value.length === 0; - } else { - return value === '' || value == null; - } - } -} - -export function WithAdditionalFilterSections() { - const [accountStatus, setAccountStatus] = useState(null); - const [accountId, setAccountId] = useState(null); - const [moneySpent, setMoneySpent] = useState(null); - const [taggedWith, setTaggedWith] = useState(null); - const [queryValue, setQueryValue] = useState(null); - - const handleAccountStatusChange = useCallback( - (value) => setAccountStatus(value), - [], - ); - const handleAccountIdChange = useCallback((value) => setAccountId(value), []); - const handleMoneySpentChange = useCallback( - (value) => setMoneySpent(value), - [], - ); - const handleTaggedWithChange = useCallback( - (value) => setTaggedWith(value), - [], - ); - const handleFiltersQueryChange = useCallback( - (value) => setQueryValue(value), - [], - ); - const handleAccountStatusRemove = useCallback( - () => setAccountStatus(null), - [], - ); - const handleAccountIdRemove = useCallback(() => setAccountId(null), []); - const handleMoneySpentRemove = useCallback(() => setMoneySpent(null), []); - const handleTaggedWithRemove = useCallback(() => setTaggedWith(null), []); - const handleQueryValueRemove = useCallback(() => setQueryValue(null), []); - const handleFiltersClearAll = useCallback(() => { - handleAccountStatusRemove(); - handleAccountIdRemove(); - handleMoneySpentRemove(); - handleTaggedWithRemove(); - handleQueryValueRemove(); - }, [ - handleAccountStatusRemove, - handleAccountIdRemove, - handleMoneySpentRemove, - handleQueryValueRemove, - handleTaggedWithRemove, - ]); - - const filters = [ - { - key: 'taggedWith', - label: 'Tagged with', - filter: ( - - ), - }, - { - key: 'accountStatus', - label: 'Account status', - section: 'Account filters', - filter: ( - - ), - }, - { - key: 'accountId', - label: 'Account ID', - section: 'Account filters', - filter: ( - - ), - }, - { - key: 'moneySpent', - label: 'Money spent', - section: 'Money filters', - filter: ( - - ), - }, - ]; - - const appliedFilters = []; - if (!isEmpty(accountStatus)) { - const key = 'accountStatus'; - appliedFilters.push({ - key, - label: disambiguateLabel(key, accountStatus), - onRemove: handleAccountStatusRemove, - }); - } - if (!isEmpty(accountId)) { - const key = 'accountId'; - appliedFilters.push({ - key, - label: disambiguateLabel(key, accountId), - onRemove: handleAccountIdRemove, - }); - } - if (!isEmpty(moneySpent)) { - const key = 'moneySpent'; - appliedFilters.push({ - key, - label: disambiguateLabel(key, moneySpent), - onRemove: handleMoneySpentRemove, - }); - } - if (!isEmpty(taggedWith)) { - const key = 'taggedWith'; - appliedFilters.push({ - key, - label: disambiguateLabel(key, taggedWith), - onRemove: handleTaggedWithRemove, - }); - } - - return ( -
- - - } - flushFilters - items={[ - { - id: 341, - url: '#', - name: 'Mae Jemison', - location: 'Decatur, USA', - }, - { - id: 256, - url: '#', - name: 'Ellen Ochoa', - location: 'Los Angeles, USA', - }, - ]} - renderItem={(item) => { - const {id, url, name, location} = item; - const media = ; - - return ( - - - {name} - -
{location}
-
- ); - }} - /> -
-
- ); - - function disambiguateLabel(key, value) { - switch (key) { - case 'moneySpent': - return `Money spent is between $${value[0]} and $${value[1]}`; - case 'taggedWith': - return `Tagged with ${value}`; - case 'accountStatus': - return value.map((val) => `Customer ${val}`).join(', '); - case 'accountId': - return `Account id: ${value}`; - default: - return value; - } - } - - function isEmpty(value) { - if (Array.isArray(value)) { - return value.length === 0; - } else { - return value === '' || value == null; - } - } -} diff --git a/polaris-react/src/components/AlphaFilters/AlphaFilters.tsx b/polaris-react/src/components/AlphaFilters/AlphaFilters.tsx deleted file mode 100644 index a949041d31f..00000000000 --- a/polaris-react/src/components/AlphaFilters/AlphaFilters.tsx +++ /dev/null @@ -1,444 +0,0 @@ -import React, {useState, useRef, useEffect, useMemo} from 'react'; -import type {ReactNode} from 'react'; -import {PlusMinor} from '@shopify/polaris-icons'; -import type {TransitionStatus} from 'react-transition-group'; - -import {useI18n} from '../../utilities/i18n'; -import {Popover} from '../Popover'; -import {ActionList} from '../ActionList'; -import {Text} from '../Text'; -import {UnstyledButton} from '../UnstyledButton'; -import {classNames} from '../../utilities/css'; -import type { - ActionListItemDescriptor, - AppliedFilterInterface, - FilterInterface, -} from '../../types'; -import {HorizontalStack} from '../HorizontalStack'; -import {Box} from '../Box'; -import {Spinner} from '../Spinner'; -import {Button} from '../Button'; - -import {FilterPill, SearchField} from './components'; -import styles from './AlphaFilters.scss'; - -const TRANSITION_DURATION = 'var(--p-motion-duration-150)'; -const TRANSITION_MARGIN = '-36px'; - -const defaultStyle = { - transition: `opacity ${TRANSITION_DURATION} var(--p-motion-ease)`, - opacity: 0, -}; - -const transitionStyles = { - entering: {opacity: 1}, - entered: {opacity: 1}, - exiting: {opacity: 0}, - exited: {opacity: 0}, - unmounted: {opacity: 0}, -}; - -const defaultFilterStyles = { - transition: `opacity ${TRANSITION_DURATION} var(--p-motion-ease), margin ${TRANSITION_DURATION} var(--p-motion-ease)`, - opacity: 0, - marginTop: TRANSITION_MARGIN, -}; - -const transitionFilterStyles = { - entering: { - opacity: 1, - marginTop: 0, - }, - entered: { - opacity: 1, - marginTop: 0, - }, - exiting: { - opacity: 0, - marginTop: TRANSITION_MARGIN, - }, - exited: { - opacity: 0, - marginTop: TRANSITION_MARGIN, - }, - unmounted: { - opacity: 0, - marginTop: TRANSITION_MARGIN, - }, -}; - -export interface AlphaFiltersProps { - /** Currently entered text in the query field */ - queryValue?: string; - /** Placeholder text for the query field. */ - queryPlaceholder?: string; - /** Whether the query field is focused. */ - focused?: boolean; - /** Available filters added to the filter bar. Shortcut filters are pinned to the front of the bar. */ - filters: FilterInterface[]; - /** Applied filters which are rendered as filter pills. The remove callback is called with the respective key. */ - appliedFilters?: AppliedFilterInterface[]; - /** Callback when the query field is changed. */ - onQueryChange: (queryValue: string) => void; - /** Callback when the clear button is triggered. */ - onQueryClear: () => void; - /** Callback when the reset all button is pressed. */ - onClearAll: () => void; - /** Callback when the query field is blurred. */ - onQueryBlur?: () => void; - /** Callback when the query field is focused. */ - onQueryFocus?: () => void; - /** The content to display inline with the controls. */ - children?: ReactNode; - /** Disable all filters. */ - disabled?: boolean; - /** Hide filter bar for applied filters. */ - hideFilters?: boolean; - /** Hide the query field. */ - hideQueryField?: boolean; - /** Disable the query field. */ - disableQueryField?: boolean; - /** Disable the filters */ - disableFilters?: boolean; - /** Whether the text field should be borderless. Should be true when used as part of the IndexFilters component. */ - borderlessQueryField?: boolean; - /** Whether an asyncronous task is currently being run. */ - loading?: boolean; - mountedState?: TransitionStatus; - /** Callback when the add filter button is clicked. */ - onAddFilterClick?: () => void; -} - -export function AlphaFilters({ - queryValue, - queryPlaceholder, - focused, - filters, - appliedFilters, - onQueryChange, - onQueryClear, - onQueryBlur, - onQueryFocus, - onClearAll, - children, - disabled, - hideFilters, - hideQueryField, - disableQueryField, - borderlessQueryField, - loading, - disableFilters, - mountedState, - onAddFilterClick, -}: AlphaFiltersProps) { - const i18n = useI18n(); - const [popoverActive, setPopoverActive] = useState(false); - const [localPinnedFilters, setLocalPinnedFilters] = useState([]); - const hasMounted = useRef(false); - - useEffect(() => { - hasMounted.current = true; - }); - - const togglePopoverActive = () => - setPopoverActive((popoverActive) => !popoverActive); - - const handleAddFilterClick = () => { - onAddFilterClick?.(); - togglePopoverActive(); - }; - const appliedFilterKeys = appliedFilters?.map(({key}) => key); - - const pinnedFiltersFromPropsAndAppliedFilters = filters.filter( - ({pinned, key}) => - (Boolean(pinned) || appliedFilterKeys?.includes(key)) && - // Filters that are pinned in local state display at the end of our list - !localPinnedFilters.find((filterKey) => filterKey === key), - ); - const pinnedFiltersFromLocalState = localPinnedFilters - .map((key) => filters.find((filter) => filter.key === key)) - .reduce( - (acc, filter) => (filter ? [...acc, filter] : acc), - [], - ); - - const pinnedFilters = [ - ...pinnedFiltersFromPropsAndAppliedFilters, - ...pinnedFiltersFromLocalState, - ]; - - const onFilterClick = - ({key, onAction}: FilterInterface) => - () => { - // PopoverOverlay will cause a rerender of the component and nuke the - // popoverActive state, so we set this as a microtask - setTimeout(() => { - setLocalPinnedFilters((currentLocalPinnedFilters) => [ - ...new Set([...currentLocalPinnedFilters, key]), - ]); - onAction?.(); - togglePopoverActive(); - }, 0); - }; - - const filterToActionItem = (filter: FilterInterface) => ({ - ...filter, - content: filter.label, - onAction: onFilterClick(filter), - }); - - const unpinnedFilters = filters.filter( - (filter) => !pinnedFilters.some(({key}) => key === filter.key), - ); - - const unsectionedFilters = unpinnedFilters - .filter((filter) => !filter.section) - .map(filterToActionItem); - - const sectionedFilters = unpinnedFilters - .filter((filter) => filter.section) - .reduce( - (acc, filter) => { - const filterActionItem = filterToActionItem(filter); - const sectionIndex = acc.findIndex( - (section) => section.title === filter.section, - ); - - if (sectionIndex === -1) { - acc.push({ - title: filter.section!, - items: [filterActionItem], - }); - } else { - acc[sectionIndex].items.push(filterActionItem); - } - - return acc; - }, - [] as { - title: string; - items: ActionListItemDescriptor[]; - }[], - ); - - const hasOneOrMorePinnedFilters = pinnedFilters.length >= 1; - - const addFilterActivator = ( -
- - - {i18n.translate('Polaris.Filters.addFilter')} - - - -
- ); - - const handleClearAllFilters = () => { - setLocalPinnedFilters([]); - onClearAll?.(); - }; - - const shouldShowAddButton = filters.some((filter) => !filter.pinned); - - const additionalContent = useMemo(() => { - return ( - <> -
- {loading ? : null} -
- {children} - - ); - }, [loading, children]); - - const queryFieldMarkup = hideQueryField ? null : ( -
- - -
- -
- {additionalContent} -
-
-
- ); - - const mountedStateStyles = - mountedState && !hideQueryField - ? { - ...defaultFilterStyles, - ...transitionFilterStyles[mountedState], - } - : undefined; - - const pinnedFiltersMarkup = pinnedFilters.map( - ({key: filterKey, ...pinnedFilter}) => { - const appliedFilter = appliedFilters?.find(({key}) => key === filterKey); - const handleFilterPillRemove = () => { - setLocalPinnedFilters((currentLocalPinnedFilters) => - currentLocalPinnedFilters.filter((key) => key !== filterKey), - ); - appliedFilter?.onRemove(filterKey); - }; - - return ( - - ); - }, - ); - - const addButton = shouldShowAddButton ? ( -
- - - -
- ) : null; - - const clearAllMarkup = - appliedFilters?.length || localPinnedFilters.length ? ( -
- -
- ) : null; - - const filtersMarkup = - hideFilters || filters.length === 0 ? null : ( -
-
-
- {pinnedFiltersMarkup} - {addButton} - {clearAllMarkup} -
-
- {hideQueryField ? ( - - - {additionalContent} - - - ) : null} -
- ); - - return ( -
- {queryFieldMarkup} - {filtersMarkup} -
- ); -} diff --git a/polaris-react/src/components/AlphaFilters/components/index.ts b/polaris-react/src/components/AlphaFilters/components/index.ts deleted file mode 100644 index 2bb64cad0c9..00000000000 --- a/polaris-react/src/components/AlphaFilters/components/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './FilterPill'; -export * from './SearchField'; diff --git a/polaris-react/src/components/AlphaFilters/index.ts b/polaris-react/src/components/AlphaFilters/index.ts deleted file mode 100644 index ad18522d359..00000000000 --- a/polaris-react/src/components/AlphaFilters/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './AlphaFilters'; -export * from './components'; diff --git a/polaris-react/src/components/AlphaFilters/tests/AlphaFilters.test.tsx b/polaris-react/src/components/AlphaFilters/tests/AlphaFilters.test.tsx deleted file mode 100644 index 5471b18e5ec..00000000000 --- a/polaris-react/src/components/AlphaFilters/tests/AlphaFilters.test.tsx +++ /dev/null @@ -1,324 +0,0 @@ -import React from 'react'; -import {matchMedia} from '@shopify/jest-dom-mocks'; -import {mountWithApp} from 'tests/utilities'; - -import {ActionList} from '../../ActionList'; -import {AlphaFilters} from '../AlphaFilters'; -import type {AlphaFiltersProps} from '../AlphaFilters'; -import {FilterPill} from '../components'; - -describe('', () => { - let originalScroll: any; - - beforeEach(() => { - originalScroll = HTMLElement.prototype.scroll; - matchMedia.mock(); - }); - - afterEach(() => { - HTMLElement.prototype.scroll = originalScroll; - matchMedia.restore(); - }); - - const defaultProps: AlphaFiltersProps = { - filters: [ - { - key: 'foo', - label: 'Foo', - pinned: false, - filter:
Filter
, - }, - { - key: 'bar', - label: 'Bar', - pinned: true, - filter:
Filter
, - }, - { - key: 'baz', - label: 'Baz', - pinned: false, - filter:
Filter
, - }, - ], - appliedFilters: [], - onQueryChange: jest.fn(), - onQueryClear: jest.fn(), - onClearAll: jest.fn(), - }; - - it('renders a list of pinned filters', () => { - const scrollSpy = jest.fn(); - HTMLElement.prototype.scroll = scrollSpy; - const wrapper = mountWithApp(); - - expect(wrapper).toContainReactComponentTimes(FilterPill, 1); - expect(wrapper).toContainReactComponent(FilterPill, { - label: defaultProps.filters[1].label, - }); - }); - - it('renders the unpinned filters inside a Popover', () => { - const scrollSpy = jest.fn(); - HTMLElement.prototype.scroll = scrollSpy; - const wrapper = mountWithApp(); - - wrapper.act(() => { - wrapper - .find('button', { - 'aria-label': 'Add filter', - })! - .trigger('onClick'); - }); - - expect(wrapper).toContainReactComponent(ActionList, { - items: [ - expect.objectContaining({content: defaultProps.filters[0].label}), - expect.objectContaining({content: defaultProps.filters[2].label}), - ], - }); - }); - - it('renders the unpinned disabled filters inside a Popover', () => { - const scrollSpy = jest.fn(); - HTMLElement.prototype.scroll = scrollSpy; - const filters = [ - ...defaultProps.filters, - { - key: 'disabled', - label: 'Disabled', - pinned: false, - disabled: true, - filter:
Filter
, - }, - ]; - - const wrapper = mountWithApp( - , - ); - - wrapper.act(() => { - wrapper - .find('button', { - 'aria-label': 'Add filter', - })! - .trigger('onClick'); - }); - - expect(wrapper).toContainReactComponent(ActionList, { - items: [ - expect.objectContaining({ - content: filters[0].label, - }), - expect.objectContaining({ - content: filters[2].label, - }), - expect.objectContaining({ - content: filters[3].label, - disabled: true, - }), - ], - }); - }); - - it('renders an applied filter', () => { - const scrollSpy = jest.fn(); - HTMLElement.prototype.scroll = scrollSpy; - const appliedFilters = [ - { - ...defaultProps.filters[2], - label: 'Bux', - value: ['Bux'], - onRemove: jest.fn(), - }, - ]; - const wrapper = mountWithApp( - , - ); - - expect(wrapper).toContainReactComponentTimes(FilterPill, 2); - expect(wrapper.findAll(FilterPill)[1]).toHaveReactProps({ - label: 'Bux', - selected: true, - }); - }); - - it('will not open the popover for an applied filter by default', () => { - const appliedFilters = [ - { - ...defaultProps.filters[2], - label: 'Bux', - value: ['Bux'], - onRemove: jest.fn(), - }, - ]; - const wrapper = mountWithApp( - , - ); - - expect(wrapper).toContainReactComponentTimes(FilterPill, 2); - expect(wrapper.findAll(FilterPill)[1]).toHaveReactProps({ - label: 'Bux', - initialActive: false, - }); - }); - - it('triggers the onAddFilterClick callback when the add filter button is clicked', () => { - const callbackFunction = jest.fn(); - const wrapper = mountWithApp( - , - ); - wrapper.act(() => { - wrapper - .find('button', { - 'aria-label': 'Add filter', - })! - .trigger('onClick'); - }); - - expect(callbackFunction).toHaveBeenCalled(); - expect(wrapper).toContainReactComponent(ActionList); - }); - - it('correctly invokes the onRemove callback when clicking on an applied filter', () => { - const scrollSpy = jest.fn(); - HTMLElement.prototype.scroll = scrollSpy; - const appliedFilters = [ - { - ...defaultProps.filters[2], - label: 'Bux', - value: ['Bux'], - onRemove: jest.fn(), - }, - ]; - const wrapper = mountWithApp( - , - ); - - wrapper.act(() => { - wrapper.findAll(FilterPill)[1].findAll('button')[1].trigger('onClick'); - }); - - expect(appliedFilters[0].onRemove).toHaveBeenCalled(); - }); - - it('will not render the add badge if all filters are pinned by default', () => { - const scrollSpy = jest.fn(); - HTMLElement.prototype.scroll = scrollSpy; - const filters = defaultProps.filters.map((filter) => ({ - ...filter, - pinned: true, - })); - - const wrapper = mountWithApp( - , - ); - - expect(wrapper).not.toContainReactComponent('div', { - className: 'AddFilterActivator', - }); - }); - - it('will not render a disabled filter if pinned', () => { - const scrollSpy = jest.fn(); - HTMLElement.prototype.scroll = scrollSpy; - const filters = [ - ...defaultProps.filters, - { - key: 'disabled', - label: 'Disabled', - pinned: true, - disabled: true, - filter:
Filter
, - }, - ]; - - const wrapper = mountWithApp( - , - ); - - expect(wrapper).toContainReactComponentTimes(FilterPill, 2); - - wrapper.act(() => { - wrapper - .find('button', { - 'aria-label': 'Add filter', - })! - .trigger('onClick'); - }); - - expect(wrapper).toContainReactComponent(ActionList, { - items: [ - expect.objectContaining({content: defaultProps.filters[0].label}), - expect.objectContaining({content: defaultProps.filters[2].label}), - ], - }); - - expect(wrapper.findAll(FilterPill)[1].domNode).toBeNull(); - }); - - it('renders filters with sections', () => { - const filtersWithSections = [ - { - key: 'sectionfilter1', - label: 'SF1', - pinned: false, - filter:
SF1
, - section: 'Section One', - }, - { - key: 'sectionfilter2', - label: 'SF2', - pinned: false, - filter:
SF1
, - section: 'Section Two', - }, - { - key: 'sectionfilter3', - label: 'SF3', - pinned: false, - filter:
SF3
, - section: 'Section One', - }, - ]; - - const wrapper = mountWithApp( - , - ); - - wrapper.act(() => { - wrapper - .find('button', { - 'aria-label': 'Add filter', - })! - .trigger('onClick'); - }); - - expect(wrapper).toContainReactComponent(ActionList, { - sections: [ - expect.objectContaining({ - title: 'Section One', - items: [ - expect.objectContaining({ - content: filtersWithSections[0].label, - }), - expect.objectContaining({ - content: filtersWithSections[2].label, - }), - ], - }), - expect.objectContaining({ - title: 'Section Two', - items: [ - expect.objectContaining({ - content: filtersWithSections[1].label, - }), - ], - }), - ], - }); - }); -}); diff --git a/polaris-react/src/components/AlphaTabs/AlphaTabs.scss b/polaris-react/src/components/AlphaTabs/AlphaTabs.scss deleted file mode 100644 index 99cdd158316..00000000000 --- a/polaris-react/src/components/AlphaTabs/AlphaTabs.scss +++ /dev/null @@ -1,306 +0,0 @@ -@import '../../styles/common'; - -:root { - --item-min-height: var(--p-space-4); - --item-min-width: 50px; - --item-vertical-padding: var(--p-space-2); -} - -.Outer { - @media #{$p-breakpoints-md-down} { - max-width: 100%; - overflow: hidden; - height: 56px; - padding: 0; - } -} - -.Wrapper { - @media #{$p-breakpoints-md-down} { - overflow: auto; - -webkit-overflow-scrolling: touch; - padding: var(--p-space-3) var(--p-space-3) var(--p-space-5) var(--p-space-4); - } -} - -.WrapperWithNewButton { - @media #{$p-breakpoints-md-up} { - position: relative; - display: inline-flex; - padding-right: var(--p-space-8); - } -} - -.ButtonWrapper { - @media #{$p-breakpoints-md-down} { - display: flex; - align-items: center; - justify-content: flex-start; - } -} - -.AlphaTabs { - display: flex; - align-items: center; - justify-content: flex-start; - margin: 0; - padding: 0; - list-style: none; - gap: var(--p-space-1); - - @media #{$p-breakpoints-md-up} { - padding: 0 var(--p-space-1); - flex-wrap: wrap; - align-items: stretch; - } -} - -.Tab { - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - @include focus-ring; - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); - position: relative; - display: inline-flex; - justify-content: center; - align-items: center; - height: var(--p-space-8); - padding: 0 var(--p-space-3); - border: 0; - border-radius: var(--p-border-radius-1); - background: var(--p-color-bg); - color: var(--p-color-text-subdued); - cursor: pointer; - text-decoration: none; - width: 100%; - min-width: 100%; // IE11 fix for overflowing flex items from parent container - margin-top: var(--p-space-025); - margin-bottom: calc(-1 * var(--p-space-025)); - outline: none; - text-align: center; - white-space: nowrap; - - &[aria-disabled='true'] { - cursor: default; - color: var(--p-color-text-disabled); - } - - &:not([aria-disabled='true']):hover, - &:not([aria-disabled='true']):focus { - background-color: var(--p-color-bg-hover); - color: var(--p-color-text); - } - - // stylelint-disable-next-line selector-max-specificity -- generated by polaris-migrator DO NOT COPY - &:not([aria-disabled='true']):focus-visible:not(:active) { - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - @include focus-ring($style: 'focused'); - outline: 0; - } - - &:not([aria-disabled='true']):active { - background-color: var(--p-color-bg-active); - color: var(--p-color-text); - z-index: var(--p-z-index-1); - } - - path { - fill: currentColor; - } - - &[aria-disabled='true'] path { - fill: var(--p-color-icon-disabled); - } -} - -.Tab-active { - background: var(--p-color-bg-primary-subdued-hover); - color: var(--p-color-text-primary); - - &[aria-disabled='true'] { - background: var(--p-color-bg-disabled); - color: var(--p-color-text-disabled); - } - - &:not([aria-disabled='true']):hover, - &:not([aria-disabled='true']):focus { - background-color: var(--p-color-bg-primary-subdued-hover); - color: var(--p-color-text-primary-hover); - } - - &:not([aria-disabled='true']):active { - background-color: var(--p-color-bg-success-subdued-active); - color: var(--p-color-text-primary); - } -} - -.Tab-hasActions { - padding-right: var(--p-space-2); -} - -.Tab-iconOnly { - padding-left: var(--p-space-1); - padding-right: var(--p-space-1); - width: var(--p-space-8); -} - -.fillSpace { - .TabContainer { - flex: 1 1 auto; - } -} - -.fitted { - flex-wrap: nowrap; - - .TabContainer { - flex: 1 1 100%; - } -} - -.TabContainer { - display: flex; - margin: 0; - padding: 0; -} - -.titleWithIcon { - display: flex; -} - -.List { - list-style: none; - margin: 0; - padding: var(--p-space-2); -} - -.Item { - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - @include unstyled-button; - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - @include focus-ring; - display: block; - width: 100%; - min-height: var(--item-min-height); - padding: var(--item-vertical-padding) var(--p-space-4); - text-align: left; - text-decoration: none; - cursor: pointer; - border-radius: var(--p-border-radius-1); - color: inherit; - - &::-moz-focus-inner { - border: none; - } - - &:hover { - background-color: var(--p-color-bg-hover); - color: var(--p-color-text); - } - - &:active { - background-color: var(--p-color-bg-active); - color: var(--p-color-text); - } - - &:focus-visible:not(:active) { - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - @include focus-ring($style: 'focused'); - } - - &:visited { - color: inherit; - } -} - -.DisclosureTab { - display: none; -} - -.DisclosureTab-visible { - display: flex; -} - -.DisclosureActivator { - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - @include focus-ring; - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - @include unstyled-button; - height: 100%; - background-color: transparent; - color: var(--p-color-text-subdued); - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - border-radius: var(--p-border-radius-1); - padding: 0 var(--p-space-2) 0 var(--p-space-3); - margin-top: var(--p-space-025); - border: none; - outline: none; - - &:hover svg, - &:focus svg { - fill: var(--p-color-icon); - } - - &:not([aria-disabled='true']):hover, - &:not([aria-disabled='true']):focus { - background-color: var(--p-color-bg-hover); - } - - &:not([aria-disabled='true']):focus-visible { - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - @include focus-ring($style: 'focused'); - outline: 0; - } - - &:not([aria-disabled='true']):active { - background-color: var(--p-color-bg-active); - z-index: var(--p-z-index-1); - } - - &[aria-disabled='true'] { - cursor: default; - color: var(--p-color-text-disabled); - - path { - fill: var(--p-color-icon-disabled); - } - } -} - -.TabsMeasurer { - display: flex; - gap: 0; - padding: 0; - visibility: hidden; - height: 0; -} - -.NewTab { - padding: 0 var(--p-space-2) 0 var(--p-space-1); - - @media #{$p-breakpoints-md-up} { - position: absolute; - right: 0; - top: 0; - padding: 0; - } -} - -.ActionListWrap { - display: block; -} - -.Panel { - display: block; - - &:focus { - outline: none; - } -} - -.Panel-hidden { - display: none; -} diff --git a/polaris-react/src/components/AlphaTabs/AlphaTabs.stories.tsx b/polaris-react/src/components/AlphaTabs/AlphaTabs.stories.tsx deleted file mode 100644 index 6b40a7e3f3c..00000000000 --- a/polaris-react/src/components/AlphaTabs/AlphaTabs.stories.tsx +++ /dev/null @@ -1,279 +0,0 @@ -import React, {useCallback, useState} from 'react'; -import type {ComponentMeta} from '@storybook/react'; -import {LegacyCard, AlphaTabs} from '@shopify/polaris'; - -export default { - component: AlphaTabs, -} as ComponentMeta; - -export function Default() { - const [selected, setSelected] = useState(0); - - const handleTabChange = (selectedTabIndex: number) => - setSelected(selectedTabIndex); - - const tabs = [ - 'All', - 'Unpaid', - 'Open', - 'Closed', - 'Local delivery', - 'Local pickup', - ].map((item, index) => ({ - content: item, - index, - id: `${item}-${index}`, - })); - - return ( - - -

Tab {selected} selected

-
-
- ); -} - -export function InsideOfACard() { - const [selected, setSelected] = useState(0); - - const handleTabChange = (selectedTabIndex: number) => - setSelected(selectedTabIndex); - - const tabs = [ - 'All', - 'Unpaid', - 'Open', - 'Closed', - 'Local delivery', - 'Local pickup', - ].map((item, index) => ({ - content: item, - index, - id: `${item}-${index}`, - })); - return ( - - - -

Tab {selected} selected

-
-
-
- ); -} - -export function Fitted() { - const [selected, setSelected] = useState(0); - - const handleTabChange = useCallback( - (selectedTabIndex: number) => setSelected(selectedTabIndex), - [], - ); - - const tabs = [ - { - id: 'all-customers-fitted-3', - content: 'All', - accessibilityLabel: 'All customers', - panelID: 'all-customers-fitted-content-3', - }, - { - id: 'accepts-marketing-fitted-3', - content: 'Accepts marketing', - panelID: 'accepts-marketing-fitted-content-3', - }, - ]; - - return ( - - - -

Tab {selected} selected

-
-
-
- ); -} - -type AlphaTabAction = - | 'rename' - | 'edit' - | 'edit-columns' - | 'duplicate' - | 'delete'; - -export function WithActions() { - const sleep = (ms: number) => - new Promise((resolve) => setTimeout(resolve, ms)); - const [selected, setSelected] = useState(0); - - const handleTabChange = (selectedTabIndex: number) => - setSelected(selectedTabIndex); - - const tabs = [ - 'All', - 'Unpaid', - 'Open', - 'Closed', - 'Local delivery', - 'Local pickup', - 'Returning customers', - 'New customers', - 'Abandoned checkouts', - 'Online store', - 'POS', - 'Facebook', - 'Instagram', - 'Twitter', - 'Pinterest', - 'Google', - 'Referral', - ].map((item, index) => ({ - content: item, - index, - id: `${item}-${index}`, - actions: - index === 0 - ? [] - : [ - { - type: 'rename' as AlphaTabAction, - onAction: () => {}, - onPrimaryAction: () => {}, - }, - { - type: 'duplicate' as AlphaTabAction, - onAction: () => {}, - onPrimaryAction: () => {}, - }, - { - type: 'edit' as AlphaTabAction, - onAction: () => {}, - onPrimaryAction: () => {}, - }, - { - type: 'delete' as AlphaTabAction, - onAction: () => {}, - onPrimaryAction: () => {}, - }, - ], - })); - - return ( - - ); -} - -export function WithBadgeContent() { - const [selected, setSelected] = useState(0); - - const handleTabChange = useCallback( - (selectedTabIndex: number) => setSelected(selectedTabIndex), - [], - ); - - const tabs = [ - { - id: 'all-customers-fitted-3', - badge: '10+', - content: 'All', - accessibilityLabel: 'All customers', - panelID: 'all-customers-fitted-content-3', - }, - { - id: 'accepts-marketing-fitted-3', - badge: '4', - content: 'Accepts marketing', - panelID: 'accepts-marketing-fitted-content-3', - }, - ]; - - return ( - - - -

Tab {selected} selected

-
-
-
- ); -} - -export function WithCustomDisclosure() { - const [selected, setSelected] = useState(0); - - const handleTabChange = useCallback( - (selectedTabIndex: number) => setSelected(selectedTabIndex), - [], - ); - - const tabs = [ - { - id: 'all-customers-4', - content: 'All', - accessibilityLabel: 'All customers', - panelID: 'all-customers-content-4', - }, - { - id: 'accepts-marketing-4', - content: 'Accepts marketing', - panelID: 'accepts-marketing-content-4', - }, - { - id: 'repeat-customers-4', - content: 'Repeat customers', - panelID: 'repeat-customers-content-4', - }, - { - id: 'prospects-4', - content: 'Prospects', - panelID: 'prospects-content-4', - }, - { - id: 'opt-out-marketing-4', - content: 'Opted out of marketing', - panelID: 'opt-out-content-4', - }, - { - id: 'net-new-customers-4', - content: 'Net new customers', - panelID: 'net-new-customers-content-4', - }, - { - id: 'churned-customers-4', - content: 'Churned customers', - panelID: 'churned=customers-content-4', - }, - ]; - - return ( - - - -

Tab {selected} selected

-
-
-
- ); -} diff --git a/polaris-react/src/components/AlphaTabs/AlphaTabs.tsx b/polaris-react/src/components/AlphaTabs/AlphaTabs.tsx deleted file mode 100644 index 426a0772c29..00000000000 --- a/polaris-react/src/components/AlphaTabs/AlphaTabs.tsx +++ /dev/null @@ -1,639 +0,0 @@ -import React, {useEffect, useCallback, useRef, useReducer} from 'react'; -import type {KeyboardEvent, FocusEvent} from 'react'; -import {CaretDownMinor, PlusMinor} from '@shopify/polaris-icons'; - -import {classNames} from '../../utilities/css'; -import {useI18n} from '../../utilities/i18n'; -import {useBreakpoints} from '../../utilities/breakpoints'; -import {Icon} from '../Icon'; -import {Popover} from '../Popover'; -import {UnstyledButton} from '../UnstyledButton'; -import {Tooltip} from '../Tooltip'; -import {Text} from '../Text'; -import {Box} from '../Box'; -import {usePrevious} from '../../utilities/use-previous'; - -import {getVisibleAndHiddenTabIndices} from './utilities'; -import type {AlphaTabProps, AlphaTabMeasurements} from './types'; -import {Tab, CreateViewModal, List, TabMeasurer, Panel} from './components'; -import styles from './AlphaTabs.scss'; - -export interface AlphaTabsState { - disclosureWidth: number; - tabWidths: number[]; - visibleTabs: number[]; - hiddenTabs: number[]; - containerWidth: number; - showDisclosure: boolean; - tabToFocus: number; - isTabPopoverOpen: boolean; - isTabModalOpen: boolean; - isNewViewModalActive: boolean; - modalSubmitted: boolean; - isTabsFocused: boolean; -} - -export interface AlphaTabsProps { - /** The items that map to each Tab. */ - tabs: AlphaTabProps[]; - /** Content to display in tabs */ - children?: React.ReactNode; - /** The index of the currently selected Tab. */ - selected: number; - /** Whether the Tabs are disabled or not. */ - disabled?: boolean; - /** Optional callback invoked when a Tab becomes selected. */ - onSelect?: (selectedTabIndex: number) => void; - /** Whether to show the add new view Tab. */ - canCreateNewView?: boolean; - /** Label for the new view Tab. Will override the default of "Create new view" */ - newViewAccessibilityLabel?: string; - /** Optional callback invoked when a merchant saves a new view from the Modal */ - onCreateNewView?: (value: string) => Promise; - /** Fit tabs to container */ - fitted?: boolean; - /** Text to replace disclosures horizontal dots */ - disclosureText?: string; -} -const CREATE_NEW_VIEW_ID = 'create-new-view'; - -export const AlphaTabs = ({ - tabs, - children, - selected, - newViewAccessibilityLabel, - canCreateNewView, - disabled, - onCreateNewView, - onSelect, - fitted, - disclosureText, -}: AlphaTabsProps) => { - const i18n = useI18n(); - - const {mdDown} = useBreakpoints(); - - const scrollRef = useRef(null); - const wrapRef = useRef(null); - const selectedTabRef = useRef(null); - - const [state, setState] = useReducer( - ( - data: AlphaTabsState, - partialData: Partial, - ): AlphaTabsState => { - return {...data, ...partialData}; - }, - { - disclosureWidth: 0, - containerWidth: Infinity, - tabWidths: [], - visibleTabs: [], - hiddenTabs: [], - showDisclosure: false, - tabToFocus: -1, - isNewViewModalActive: false, - modalSubmitted: false, - isTabsFocused: false, - isTabPopoverOpen: false, - isTabModalOpen: false, - }, - ); - - const { - tabToFocus, - visibleTabs, - hiddenTabs, - showDisclosure, - isNewViewModalActive, - modalSubmitted, - disclosureWidth, - tabWidths, - containerWidth, - isTabsFocused, - isTabModalOpen, - isTabPopoverOpen, - } = state; - - const prevModalOpen = usePrevious(isTabModalOpen); - const prevPopoverOpen = usePrevious(isTabPopoverOpen); - - useEffect(() => { - const hasModalClosed = prevModalOpen && !isTabModalOpen; - const hasPopoverClosed = prevPopoverOpen && !isTabPopoverOpen; - if (hasModalClosed) { - setState({isTabsFocused: true, tabToFocus: selected}); - } else if (hasPopoverClosed && !isTabModalOpen) { - setState({isTabsFocused: true, tabToFocus: selected}); - } - }, [ - prevPopoverOpen, - isTabPopoverOpen, - prevModalOpen, - isTabModalOpen, - selected, - tabToFocus, - ]); - - const handleTogglePopover = useCallback( - (isOpen: boolean) => setState({isTabPopoverOpen: isOpen}), - [], - ); - - const handleToggleModal = useCallback( - (isOpen: boolean) => setState({isTabModalOpen: isOpen}), - [], - ); - - const handleCloseNewViewModal = () => { - setState({ - isNewViewModalActive: false, - }); - }; - - const handleSaveNewViewModal = async (value: string) => { - if (!onCreateNewView) { - return false; - } - const hasExecuted = await onCreateNewView?.(value); - if (hasExecuted) { - setState({ - modalSubmitted: true, - }); - } - return hasExecuted; - }; - - const handleClickNewTab = () => { - setState({ - isNewViewModalActive: true, - }); - }; - - const handleTabClick = useCallback( - (id: string) => { - const tab = tabs.find((aTab) => aTab.id === id); - if (tab == null) { - return null; - } - const selectedIndex = tabs.indexOf(tab); - onSelect?.(selectedIndex); - }, - [tabs, onSelect], - ); - - const renderTabMarkup = useCallback( - (tab: AlphaTabProps, index: number) => { - const handleClick = () => { - handleTabClick(tab.id); - tab.onAction?.(); - }; - - const viewNames = tabs.map(({content}) => content); - const tabPanelID = tab.panelID || `${tab.id}-panel`; - - return ( - -1} - focused={index === tabToFocus} - selected={index === selected} - onAction={handleClick} - accessibilityLabel={tab.accessibilityLabel} - url={tab.url} - content={tab.content} - onToggleModal={handleToggleModal} - onTogglePopover={handleTogglePopover} - viewNames={viewNames} - ref={index === selected ? selectedTabRef : null} - /> - ); - }, - [ - disabled, - handleTabClick, - tabs, - children, - selected, - tabToFocus, - handleToggleModal, - handleTogglePopover, - ], - ); - - const handleFocus = useCallback((event: FocusEvent) => { - const target = event.target; - const isItem = target.classList.contains(styles.Item); - const isInNaturalDOMOrder = - target.closest(`[data-tabs-focus-catchment]`) || isItem; - - const isDisclosureActivator = target.classList.contains( - styles.DisclosureActivator, - ); - - if (isDisclosureActivator || !isInNaturalDOMOrder) { - return; - } - - setState({ - isTabsFocused: true, - }); - }, []); - - const handleBlur = useCallback( - (event: FocusEvent) => { - const target = event.target; - const relatedTarget = event.relatedTarget; - const isInNaturalDOMOrder = relatedTarget?.closest?.( - `.${styles.AlphaTabs}`, - ); - const targetIsATab = target?.classList?.contains?.(styles.Tab); - const focusReceiverIsAnItem = relatedTarget?.classList.contains( - styles.Item, - ); - - if ( - !relatedTarget && - !isTabModalOpen && - !targetIsATab && - !focusReceiverIsAnItem - ) { - setState({ - tabToFocus: -1, - }); - return; - } - - if ( - !isInNaturalDOMOrder && - !isTabModalOpen && - !targetIsATab && - !focusReceiverIsAnItem - ) { - setState({ - tabToFocus: -1, - }); - return; - } - - setState({ - isTabsFocused: false, - }); - }, - [isTabModalOpen], - ); - - const handleKeyDown = (event: KeyboardEvent) => { - if (isTabPopoverOpen || isTabModalOpen || isNewViewModalActive) { - return; - } - const {key} = event; - - if (key === 'ArrowLeft' || key === 'ArrowRight') { - event.preventDefault(); - event.stopPropagation(); - } - }; - - useEffect(() => { - const {visibleTabs, hiddenTabs} = getVisibleAndHiddenTabIndices( - tabs, - selected, - disclosureWidth, - tabWidths, - containerWidth, - ); - setState({ - visibleTabs, - hiddenTabs, - }); - }, [containerWidth, disclosureWidth, tabs, selected, tabWidths, setState]); - - const moveToSelectedTab = useCallback(() => { - const activeButton = selectedTabRef.current?.querySelector( - `.${styles['Tab-active']}`, - ) as HTMLElement; - if (activeButton) { - moveToActiveTab(activeButton.offsetLeft); - } - }, []); - - useEffect(() => { - if (mdDown) { - moveToSelectedTab(); - } - }, [moveToSelectedTab, selected, mdDown]); - - useEffect(() => { - if (isTabsFocused && !showDisclosure) { - const tabToFocus = selected; - setState({tabToFocus}); - } - }, [isTabsFocused, selected, setState, showDisclosure]); - - const handleKeyPress = (event: KeyboardEvent) => { - const { - showDisclosure, - visibleTabs, - hiddenTabs, - tabToFocus, - isNewViewModalActive, - } = state; - if (isTabModalOpen || isTabPopoverOpen || isNewViewModalActive) { - return; - } - const key = event.key; - const tabsArrayInOrder = - showDisclosure || mdDown - ? visibleTabs.concat(hiddenTabs) - : [...visibleTabs]; - - let newFocus = tabsArrayInOrder.indexOf(tabToFocus); - - if (key === 'ArrowRight') { - newFocus += 1; - - if (newFocus === tabsArrayInOrder.length) { - newFocus = 0; - } - } - - if (key === 'ArrowLeft') { - if (newFocus === -1 || newFocus === 0) { - newFocus = tabsArrayInOrder.length - 1; - } else { - newFocus -= 1; - } - } - - const buttonToFocus = tabsArrayInOrder[newFocus]; - - if (buttonToFocus != null) { - setState({ - tabToFocus: buttonToFocus, - }); - } - }; - - const handleDisclosureActivatorClick = () => { - setState({ - showDisclosure: !showDisclosure, - tabToFocus: hiddenTabs[0], - }); - }; - - const handleClose = () => { - setState({showDisclosure: false}); - }; - - const handleMeasurement = useCallback( - (measurements: AlphaTabMeasurements) => { - const { - hiddenTabWidths: tabWidths, - containerWidth, - disclosureWidth, - } = measurements; - - const {visibleTabs, hiddenTabs} = getVisibleAndHiddenTabIndices( - tabs, - selected, - disclosureWidth, - tabWidths, - containerWidth, - ); - setState({ - visibleTabs, - hiddenTabs, - disclosureWidth, - containerWidth, - tabWidths, - }); - }, - [tabs, selected, setState], - ); - - const handleListTabClick = (id: string) => { - handleTabClick(id); - handleClose(); - setState({ - isTabsFocused: true, - }); - }; - - const moveToActiveTab = (offsetLeft: number) => { - setTimeout(() => { - if (scrollRef.current && typeof scrollRef.current.scroll === 'function') { - const scrollRefOffset = wrapRef?.current?.offsetLeft || 0; - scrollRef?.current?.scroll({ - left: offsetLeft - scrollRefOffset, - }); - } - }, 0); - }; - - const createViewA11yLabel = - newViewAccessibilityLabel || - i18n.translate('Polaris.Tabs.newViewAccessibilityLabel'); - - const tabsToShow = mdDown ? [...visibleTabs, ...hiddenTabs] : visibleTabs; - - const tabsMarkup = tabsToShow - .sort((tabA, tabB) => tabA - tabB) - .filter((tabIndex) => tabs[tabIndex]) - .map((tabIndex) => renderTabMarkup(tabs[tabIndex], tabIndex)); - - const disclosureActivatorVisible = - visibleTabs.length < tabs.length && !mdDown; - - const classname = classNames( - styles.AlphaTabs, - fitted && styles.fitted, - disclosureActivatorVisible && styles.fillSpace, - ); - - const wrapperClassNames = classNames( - styles.Wrapper, - canCreateNewView && styles.WrapperWithNewButton, - ); - - const disclosureTabClassName = classNames( - styles.DisclosureTab, - disclosureActivatorVisible && styles['DisclosureTab-visible'], - ); - - const disclosureButtonClassName = classNames(styles.DisclosureActivator); - - const disclosureButtonContent = ( - <> - - {disclosureText ?? i18n.translate('Polaris.Tabs.toggleTabsLabel')} - -
- -
- - ); - - const disclosureButton = ( - - {disclosureButtonContent} - - ); - - const activator = disclosureButton; - - const disclosureTabs = hiddenTabs.map((tabIndex) => tabs[tabIndex]); - - const viewNames = tabs.map(({content}) => content); - - const tabMeasurer = ( - -1} - handleMeasurement={handleMeasurement} - /> - ); - - const newTab = ( - { - if (modalSubmitted) { - setState({ - tabToFocus: selected, - modalSubmitted: false, - }); - } - }} - icon={ - - } - disabled={disabled} - onTogglePopover={handleTogglePopover} - onToggleModal={handleToggleModal} - tabIndexOverride={0} - /> - ); - - const panelMarkup = children - ? tabs.map((_tab, index) => { - return selected === index ? ( - - {children} - - ) : ( -
); } diff --git a/polaris-react/src/components/Breadcrumbs/Breadcrumbs.tsx b/polaris-react/src/components/Breadcrumbs/Breadcrumbs.tsx index 8c11f72bb8e..39f68a2673e 100644 --- a/polaris-react/src/components/Breadcrumbs/Breadcrumbs.tsx +++ b/polaris-react/src/components/Breadcrumbs/Breadcrumbs.tsx @@ -10,29 +10,12 @@ import {Text} from '../Text'; import styles from './Breadcrumbs.scss'; export interface BreadcrumbsProps { - /** @deprecated Collection of breadcrumbs */ - breadcrumbs?: (CallbackAction | LinkAction) | (CallbackAction | LinkAction)[]; /** Back action link */ - backAction?: CallbackAction | LinkAction; + backAction: CallbackAction | LinkAction; } -export function Breadcrumbs({breadcrumbs, backAction}: BreadcrumbsProps) { - const breadcrumb = - backAction ?? - (Array.isArray(breadcrumbs) - ? breadcrumbs[breadcrumbs.length - 1] - : breadcrumbs); - if (breadcrumb == null) { - if (process.env.NODE_ENV === 'development') { - // eslint-disable-next-line no-console - console.warn( - 'Please provide a value to backAction, it will become required in the next major release.', - ); - } - return null; - } - - const {content} = breadcrumb; +export function Breadcrumbs({backAction}: BreadcrumbsProps) { + const {content} = backAction; const contentMarkup = ( <> @@ -46,13 +29,13 @@ export function Breadcrumbs({breadcrumbs, backAction}: BreadcrumbsProps) { ); const breadcrumbMarkup = - 'url' in breadcrumb ? ( + 'url' in backAction ? ( {contentMarkup} @@ -60,10 +43,10 @@ export function Breadcrumbs({breadcrumbs, backAction}: BreadcrumbsProps) { diff --git a/polaris-react/src/components/Breadcrumbs/tests/Breadcrumbs.test.tsx b/polaris-react/src/components/Breadcrumbs/tests/Breadcrumbs.test.tsx index e5033a7cc8a..766af44ee64 100644 --- a/polaris-react/src/components/Breadcrumbs/tests/Breadcrumbs.test.tsx +++ b/polaris-react/src/components/Breadcrumbs/tests/Breadcrumbs.test.tsx @@ -8,31 +8,25 @@ import {Text} from '../../Text'; describe('', () => { describe('url', () => { it('uses tags when passed a LinkAction', () => { - const linkBreadcrumbs: LinkAction[] = [ - { - content: 'Products', - url: 'https://www.shopify.com', - }, - ]; - + const linkBackAction: LinkAction = { + content: 'Products', + url: 'https://www.shopify.com', + }; const breadcrumbs = mountWithApp( - , + , ); expect(breadcrumbs).toContainReactComponentTimes('a', 1); }); it('passes the accessibilityLabel through to tag', () => { - const linkBreadcrumbs: LinkAction[] = [ - { - content: 'Products', - url: 'https://shopify.com', - accessibilityLabel: 'Go to Products', - }, - ]; - + const linkBackAction: LinkAction = { + content: 'Products', + url: 'https://shopify.com', + accessibilityLabel: 'Go to Products', + }; const breadcrumbs = mountWithApp( - , + , ); expect(breadcrumbs).toContainReactComponent('a', { @@ -43,31 +37,25 @@ describe('', () => { describe('onAction()', () => { it('uses - - -
- - - - ); -} - -export function WithDestructiveFooterAction() { - return ( - - - - 1 × Oasis Glass, 4-Pack - 1 × Anubis Cup, 2-Pack - - - - ); -} - -export function WithMultipleSections() { - return ( - - + + + + Online store dashboard +

View a summary of your online store’s performance.

-
- - -

- View a summary of your online store’s performance, including sales, - visitors, top products, and referrals. -

-
+
); } -export function WithMultipleTitledSections() { +export function WithBackgroundSubdued() { return ( - - + + + + Online store dashboard +

View a summary of your online store’s performance.

-
- - -

- View a summary of your online store’s performance, including sales, - visitors, top products, and referrals. -

-
+
); } -export function WithSectionsAndActions() { +export function WithBorderRadiusRoundedAbove() { return ( - - -

John Smith

-
- -

john.smith@example.com

-
-
- ); -} - -export function WithSubsection() { - return ( - - -

John Smith

-
- - - 123 First St -
- Somewhere -
- The Universe -
- - 123 Second St -
- Somewhere -
- The Universe -
-
- - - A single subsection without a sibling has no visual appearance - - -
- ); -} - -export function WithDestructiveAction() { - return ( - - -

John Smith

-
- -

john.smith@example.com

-
-
- ); -} - -export function WithASubduedSection() { - return ( - - - - Felix Crafford - Ezequiel Manno - - - - - - Felix Crafford - Ezequiel Manno - - + + + + Online store dashboard + +

View a summary of your online store’s performance.

+
); } -export function WithSubduedForSecondaryContent() { +export function WithResponsivePadding() { return ( - - - Felix Crafford - Ezequiel Manno - + + + + Online store dashboard + +

View a summary of your online store’s performance.

+
); } -export function WithSeparateHeader() { +export function WithSubduedSection() { return ( - - + + + Staff accounts + + + + Felix Crafford + Ezequiel Manno + + + + - - Add account - - } - onClose={() => {}} - > - - - - - - Felix Crafford - Ezequiel Manno - - - - ); -} - -export function WithCustomReactNodeTitle() { - return ( - - - + + + - New Products + Deactivated staff accounts - - } - > - - Socks - Super Shoes - - - - ); -} - -export function WithAllElements() { - return ( - - - - View Sales - - } - onClose={() => {}} - > - - - - - - You can use sales reports to see information about your customers’ - orders based on criteria such as sales over time, by channel, or by - staff. - - - - { - const {sales, amount, url} = item; - return ( - - - {sales} - {amount} - - - ); - }} - /> - - - - Payouts - Total Sales By Channel - - - - - The sales reports are available only if your store is on the Shopify - plan or higher. - - - - ); -} - -export function WithFlushedSections() { - return ( - - - a sheet with purple and orange stripes - - - - You can use sales reports to see information about your customers’ - orders based on criteria such as sales over time, by channel, or by - staff. - - + + Felix Crafford + Ezequiel Manno + + + + ); } diff --git a/polaris-react/src/components/Card/Card.tsx b/polaris-react/src/components/Card/Card.tsx index 70d7eac86b6..4c7762fda81 100644 --- a/polaris-react/src/components/Card/Card.tsx +++ b/polaris-react/src/components/Card/Card.tsx @@ -1,160 +1,59 @@ +import type { + BreakpointsAlias, + ColorBackgroundAlias, + BorderRadiusScale, + SpaceScale, +} from '@shopify/polaris-tokens'; import React from 'react'; -import {useI18n} from '../../utilities/i18n'; -import {classNames} from '../../utilities/css'; -import {useToggle} from '../../utilities/use-toggle'; -import {WithinContentContext} from '../../utilities/within-content-context'; -import {ButtonGroup} from '../ButtonGroup'; -import type {DisableableAction, ComplexAction} from '../../types'; -import {ActionList} from '../ActionList'; -import {Button, buttonFrom} from '../Button'; -import {Popover} from '../Popover'; +import {useBreakpoints} from '../../utilities/breakpoints'; +import type {ResponsiveProp} from '../../utilities/css'; +import {Box} from '../Box'; -// eslint-disable-next-line import/no-deprecated -import {Header, Section, Subsection} from './components'; -import styles from './Card.scss'; - -export type { - CardSectionProps, - CardHeaderProps, - CardSubsectionProps, -} from './components'; +type Spacing = ResponsiveProp; export interface CardProps { - /** Title content for the card */ - title?: React.ReactNode; - /** Inner content of the card */ children?: React.ReactNode; - /** A less prominent card */ - subdued?: boolean; - /** Auto wrap content in section */ - sectioned?: boolean; - /** Card header actions */ - actions?: DisableableAction[]; - /** Primary action in the card footer */ - primaryFooterAction?: ComplexAction; - /** Secondary actions in the card footer */ - secondaryFooterActions?: ComplexAction[]; - /** The content of the disclosure button rendered when there is more than one secondary footer action */ - secondaryFooterActionsDisclosureText?: string; - /** Alignment of the footer actions on the card, defaults to right */ - footerActionAlignment?: 'right' | 'left'; - /** Allow the card to be hidden when printing */ - hideOnPrint?: boolean; + /** Background color + * @default 'bg' + */ + background?: ColorBackgroundAlias; + /** The spacing around the card + * @default {xs: '4', sm: '5'} + * @example + * padding='4' + * padding={{xs: '2', sm: '3', md: '4', lg: '5', xl: '6'}} + */ + padding?: Spacing; + /** Border radius value above a set breakpoint */ + roundedAbove?: BreakpointsAlias; } -// TypeScript can't generate types that correctly infer the typing of -// subcomponents so explicitly state the subcomponents in the type definition. -// Letting this be implicit works in this project but fails in projects that use -// generated *.d.ts files. -/** @deprecated Use AlphaCard instead. */ -export const Card: React.FunctionComponent & { - // eslint-disable-next-line import/no-deprecated - Header: typeof Header; - // eslint-disable-next-line import/no-deprecated - Section: typeof Section; - // eslint-disable-next-line import/no-deprecated - Subsection: typeof Subsection; -} = function Card({ +export const Card = ({ children, - hideOnPrint, - title, - subdued, - sectioned, - actions, - primaryFooterAction, - secondaryFooterActions, - secondaryFooterActionsDisclosureText, - footerActionAlignment = 'right', -}: CardProps) { - if (process.env.NODE_ENV === 'development') { - // eslint-disable-next-line no-console - console.warn( - 'Deprecation: is deprecated. This component will be removed in a future major version of Polaris. Use or instead.', - ); - } - - const i18n = useI18n(); - const { - value: secondaryActionsPopoverOpen, - toggle: toggleSecondaryActionsPopoverOpen, - } = useToggle(false); - - const className = classNames( - styles.Card, - subdued && styles.subdued, - hideOnPrint && styles.hideOnPrint, - ); + background = 'bg', + padding = {xs: '4', sm: '5'}, + roundedAbove, +}: CardProps) => { + const breakpoints = useBreakpoints(); + const defaultBorderRadius: BorderRadiusScale = '2'; - const headerMarkup = - title || actions ?
: null; + let hasBorderRadius = !roundedAbove; - const content = sectioned ?
{children}
: children; - - const primaryFooterActionMarkup = primaryFooterAction - ? buttonFrom(primaryFooterAction, {primary: true}) - : null; - - let secondaryFooterActionsMarkup = null; - if (secondaryFooterActions && secondaryFooterActions.length) { - if (secondaryFooterActions.length === 1) { - secondaryFooterActionsMarkup = buttonFrom(secondaryFooterActions[0]); - } else { - secondaryFooterActionsMarkup = ( - <> - - {secondaryFooterActionsDisclosureText || - i18n.translate('Polaris.Common.more')} - - } - onClose={toggleSecondaryActionsPopoverOpen} - > - - - - ); - } + if (roundedAbove && breakpoints[`${roundedAbove}Up`]) { + hasBorderRadius = true; } - const footerMarkup = - primaryFooterActionMarkup || secondaryFooterActionsMarkup ? ( -
- {footerActionAlignment === 'right' ? ( - - {secondaryFooterActionsMarkup} - {primaryFooterActionMarkup} - - ) : ( - - {primaryFooterActionMarkup} - {secondaryFooterActionsMarkup} - - )} -
- ) : null; - return ( - -
- {headerMarkup} - {content} - {footerMarkup} -
-
+ + {children} + ); }; - -// eslint-disable-next-line import/no-deprecated -Card.Header = Header; -// eslint-disable-next-line import/no-deprecated -Card.Section = Section; -// eslint-disable-next-line import/no-deprecated -Card.Subsection = Subsection; diff --git a/polaris-react/src/components/Card/components/Header/Header.tsx b/polaris-react/src/components/Card/components/Header/Header.tsx deleted file mode 100644 index f1115fd9d2b..00000000000 --- a/polaris-react/src/components/Card/components/Header/Header.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import React, {isValidElement} from 'react'; - -import type {DisableableAction} from '../../../../types'; -import {buttonsFrom} from '../../../Button'; -import {ButtonGroup} from '../../../ButtonGroup'; -import {LegacyStack} from '../../../LegacyStack'; -import {Text} from '../../../Text'; -import styles from '../../Card.scss'; - -export interface CardHeaderProps { - title?: React.ReactNode; - actions?: DisableableAction[]; - children?: React.ReactNode; -} - -/** @deprecated Use LegacyCard or AlphaCard instead. */ -export function Header({children, title, actions}: CardHeaderProps) { - if (process.env.NODE_ENV === 'development') { - // eslint-disable-next-line no-console - console.warn( - 'Deprecation: is deprecated. This component will be removed in a future major version of Polaris. Use or instead.', - ); - } - - const actionMarkup = actions ? ( - {buttonsFrom(actions, {plain: true})} - ) : null; - - const titleMarkup = isValidElement(title) ? ( - title - ) : ( - - {title} - - ); - - const headingMarkup = - actionMarkup || children ? ( - - {titleMarkup} - {actionMarkup} - {children} - - ) : ( - titleMarkup - ); - - return
{headingMarkup}
; -} diff --git a/polaris-react/src/components/Card/components/Header/index.ts b/polaris-react/src/components/Card/components/Header/index.ts deleted file mode 100644 index 266dec8a1bc..00000000000 --- a/polaris-react/src/components/Card/components/Header/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Header'; diff --git a/polaris-react/src/components/Card/components/Header/tests/Header.test.tsx b/polaris-react/src/components/Card/components/Header/tests/Header.test.tsx deleted file mode 100644 index 374e49828b7..00000000000 --- a/polaris-react/src/components/Card/components/Header/tests/Header.test.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import React from 'react'; -import {mountWithApp} from 'tests/utilities'; - -import {buttonsFrom} from '../../../../Button'; -import {ButtonGroup} from '../../../../ButtonGroup'; -import {Text} from '../../../../Text'; -// eslint-disable-next-line import/no-deprecated -import {Header} from '../Header'; - -jest.mock('../../../../Button', () => ({ - ...(jest.requireActual('../../../../Button') as any), - buttonsFrom: jest.fn(), -})); - -const buttonsFromMock = buttonsFrom as jest.Mock; - -describe('
', () => { - describe('title', () => { - it('renders a heading when defined', () => { - const header = mountWithApp(
); - expect(header).toContainReactComponent(Text, {variant: 'headingMd'}); - }); - - it('renders the title directly if its a valid React element', () => { - const title =
Staff accounts
; - const header = mountWithApp(
); - expect(header).not.toContainReactComponent(Text, {variant: 'headingLg'}); - expect(header).toContainReactComponent('div', { - children: 'Staff accounts', - }); - }); - - it('is used as the content for the heading', () => { - const title = 'Staff accounts'; - const header = mountWithApp(
); - expect(header.find(Text, {variant: 'headingMd'})).toContainReactText( - title, - ); - }); - }); - - describe('actions', () => { - const mockActions = [{content: 'Preview'}]; - - it('renders a button group when defined', () => { - const header = mountWithApp(
); - expect(header).toContainReactComponent(ButtonGroup); - }); - - it('renders buttons for each action', () => { - mountWithApp(
); - expect(buttonsFromMock).toHaveBeenCalledWith( - mockActions, - expect.anything(), - ); - }); - - it('does not render a button group when not defined', () => { - const header = mountWithApp(
); - expect(header).not.toContainReactComponent(ButtonGroup); - }); - }); - - describe('children', () => { - it('renders when defined', () => { - const Children = () =>
Hello!
; - const header = mountWithApp( -
- -
, - ); - expect(header).toContainReactComponent(Children); - }); - }); -}); diff --git a/polaris-react/src/components/Card/components/Section/Section.tsx b/polaris-react/src/components/Card/components/Section/Section.tsx deleted file mode 100644 index 4c4d2bd2db4..00000000000 --- a/polaris-react/src/components/Card/components/Section/Section.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import React from 'react'; - -import {classNames} from '../../../../utilities/css'; -import type {ComplexAction} from '../../../../types'; -import {buttonsFrom} from '../../../Button'; -import {LegacyStack} from '../../../LegacyStack'; -import {ButtonGroup} from '../../../ButtonGroup'; -import {Text} from '../../../Text'; -import styles from '../../Card.scss'; - -export interface CardSectionProps { - title?: React.ReactNode; - children?: React.ReactNode; - subdued?: boolean; - flush?: boolean; - fullWidth?: boolean; - /** Allow the card to be hidden when printing */ - hideOnPrint?: boolean; - actions?: ComplexAction[]; -} - -/** @deprecated Use LegacyCard or AlphaCard instead. */ -export function Section({ - children, - title, - subdued, - flush, - fullWidth, - actions, - hideOnPrint, -}: CardSectionProps) { - if (process.env.NODE_ENV === 'development') { - // eslint-disable-next-line no-console - console.warn( - 'Deprecation: is deprecated. This component will be removed in a future major version of Polaris. Use or instead.', - ); - } - - const className = classNames( - styles.Section, - flush && styles['Section-flush'], - subdued && styles['Section-subdued'], - fullWidth && styles['Section-fullWidth'], - hideOnPrint && styles['Section-hideOnPrint'], - ); - - const actionMarkup = actions ? ( - {buttonsFrom(actions, {plain: true})} - ) : null; - - const titleMarkup = - typeof title === 'string' ? ( - - {title} - - ) : ( - title - ); - - const titleAreaMarkup = - titleMarkup || actionMarkup ? ( -
- {actionMarkup ? ( - - {titleMarkup} - {actionMarkup} - - ) : ( - titleMarkup - )} -
- ) : null; - - return ( -
- {titleAreaMarkup} - {children} -
- ); -} diff --git a/polaris-react/src/components/Card/components/Section/index.ts b/polaris-react/src/components/Card/components/Section/index.ts deleted file mode 100644 index b524e0f3c49..00000000000 --- a/polaris-react/src/components/Card/components/Section/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Section'; diff --git a/polaris-react/src/components/Card/components/Section/tests/Section.test.tsx b/polaris-react/src/components/Card/components/Section/tests/Section.test.tsx deleted file mode 100644 index cb5517f3afd..00000000000 --- a/polaris-react/src/components/Card/components/Section/tests/Section.test.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import React from 'react'; -import {mountWithApp} from 'tests/utilities'; - -import {Badge} from '../../../../Badge'; -import {Button} from '../../../../Button'; -import {ButtonGroup} from '../../../../ButtonGroup'; -import {Text} from '../../../../Text'; -// eslint-disable-next-line import/no-deprecated -import {Section} from '../Section'; - -describe('', () => { - it('can have any valid react element as the card section title', () => { - const titleString = 'Online store'; - const badgeString = 'I am a badge'; - const titleMarkup = ( -

- {titleString} - {badgeString} -

- ); - - const section = mountWithApp(
); - const headerMarkup = section.find('h2')!; - - expect(headerMarkup).toContainReactText(titleString); - expect(headerMarkup.find(Badge)).toContainReactText(badgeString); - }); - - it('wraps plain string titles in a ', () => { - const titleString = 'Online store'; - const card = mountWithApp(
); - expect( - card.find(Text, {variant: 'headingSm', as: 'h3'}), - ).toContainReactText(titleString); - }); - - describe('hideWhenPrinting prop', () => { - it('renders classname "Section Section-hideOnPrint" when prop is passed', () => { - const card = mountWithApp(
); - - expect(card).toContainReactComponent('div', { - className: 'Section Section-hideOnPrint', - }); - }); - - it('does not render classname "Section Section-hideOnPrint" when prop is not passed', () => { - const card = mountWithApp(
); - - expect(card).not.toContainReactComponent('div', { - className: 'Section Section-hideOnPrint', - }); - }); - }); - - describe('actions', () => { - const mockActions = [{content: 'Preview'}, {content: 'Open'}]; - - it('renders a button group when defined', () => { - const section = mountWithApp(
); - expect(section).toContainReactComponent(ButtonGroup); - }); - - it('renders buttons for each action', () => { - const section = mountWithApp(
); - expect(section).toContainReactComponentTimes(Button, 2); - }); - - it('does not render a button group when not defined', () => { - const section = mountWithApp(
); - expect(section).not.toContainReactComponent(ButtonGroup); - }); - - it('renders both custom title markup and actions', () => { - const titleString = 'Online store'; - const badgeString = 'I am a badge'; - const titleMarkup = ( -

- {titleString} - {badgeString} -

- ); - const section = mountWithApp( -
, - ); - expect(section).toContainReactComponentTimes(Button, 2); - expect(section).toContainReactText(titleString); - expect(section.find(Badge)).toContainReactText(badgeString); - }); - }); -}); diff --git a/polaris-react/src/components/Card/components/Subsection/Subsection.tsx b/polaris-react/src/components/Card/components/Subsection/Subsection.tsx deleted file mode 100644 index f1715e61524..00000000000 --- a/polaris-react/src/components/Card/components/Subsection/Subsection.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; - -import styles from '../../Card.scss'; - -export interface CardSubsectionProps { - children?: React.ReactNode; -} - -/** @deprecated Use LegacyCard or AlphaCard instead. */ -export function Subsection({children}: CardSubsectionProps) { - if (process.env.NODE_ENV === 'development') { - // eslint-disable-next-line no-console - console.warn( - 'Deprecation: is deprecated. This component will be removed in a future major version of Polaris. Use or instead.', - ); - } - - return
{children}
; -} diff --git a/polaris-react/src/components/Card/components/Subsection/index.ts b/polaris-react/src/components/Card/components/Subsection/index.ts deleted file mode 100644 index cddeba60629..00000000000 --- a/polaris-react/src/components/Card/components/Subsection/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Subsection'; diff --git a/polaris-react/src/components/Card/components/Subsection/tests/Subsection.test.tsx b/polaris-react/src/components/Card/components/Subsection/tests/Subsection.test.tsx deleted file mode 100644 index 353a2b9bdc9..00000000000 --- a/polaris-react/src/components/Card/components/Subsection/tests/Subsection.test.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import {mountWithApp} from 'tests/utilities'; - -// eslint-disable-next-line import/no-deprecated -import {Subsection} from '../Subsection'; - -describe('', () => { - it('can have any valid react element for children', () => { - const childrenMarkup =

Some content

; - - const section = mountWithApp({childrenMarkup}); - - expect(section).toContainReactText('Some content'); - expect(section).toContainReactComponent('p'); - }); -}); diff --git a/polaris-react/src/components/Card/components/index.ts b/polaris-react/src/components/Card/components/index.ts deleted file mode 100644 index 194aac47a90..00000000000 --- a/polaris-react/src/components/Card/components/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './Header'; - -export * from './Section'; - -export * from './Subsection'; diff --git a/polaris-react/src/components/Card/tests/Card.test.tsx b/polaris-react/src/components/Card/tests/Card.test.tsx index 4c7cdc97f0d..34fca04f868 100644 --- a/polaris-react/src/components/Card/tests/Card.test.tsx +++ b/polaris-react/src/components/Card/tests/Card.test.tsx @@ -1,253 +1,46 @@ import React from 'react'; import {mountWithApp} from 'tests/utilities'; +import {matchMedia} from '@shopify/jest-dom-mocks'; +import {setMediaWidth} from 'tests/utilities/breakpoints'; -import {Badge} from '../../Badge'; -import {Button} from '../../Button'; -import {Popover} from '../../Popover'; -import {ActionList} from '../../ActionList'; -import {WithinContentContext} from '../../../utilities/within-content-context'; -// eslint-disable-next-line import/no-deprecated -import {Card} from '../Card'; -// eslint-disable-next-line import/no-deprecated -import {Section} from '../components'; +import {Card} from '..'; -describe('', () => { - it('has a child with prop withinContentContainer set to true', () => { - function TestComponent(_: {withinContentContainer: any}) { - return null; - } +const heading =

Online store dashboard

; +const subheading =

View a summary of your online store performance

; - const component = mountWithApp( - - - {(withinContentContext) => { - return ( - - ); - }} - - , - ); - expect(component).toContainReactComponent(TestComponent, { - withinContentContainer: true, - }); +describe('Card', () => { + beforeEach(() => { + matchMedia.mock(); }); - it('has a header tag when the title is a string', () => { - const title = 'Online store'; - const card = mountWithApp(); - expect(card.find('h2')).toContainReactText(title); + afterEach(() => { + matchMedia.restore(); }); - it('can have any valid react element as the title', () => { - const titleString = 'Online store'; - const badgeString = 'I am a badge'; - const titleMarkup = ( -

- {titleString} - {badgeString} -

- ); - - const card = mountWithApp(); - const headerMarkup = card.find('h2')!; - - expect(headerMarkup).toContainReactText(titleString); - expect(headerMarkup.find(Badge)).toContainReactText(badgeString); - }); - - it('exposes the header component', () => { + it('renders children', () => { const card = mountWithApp( - - , - ); - // eslint-disable-next-line import/no-deprecated - expect(card).toContainReactComponent(Card.Header); - }); - - it('renders a
component with actions and no title', () => { - const card = mountWithApp( - -

Some card content.

+ {heading} + {subheading}
, ); - expect(card).toContainReactComponent(Button); - // eslint-disable-next-line import/no-deprecated - expect(card).toContainReactComponent(Card.Header); - }); - - describe('footerActionAlignment prop', () => { - it('renders right-aligned if not supplied', () => { - const card = mountWithApp( - -

Some card content.

-
, - ); - - const buttons = card.findAll(Button); - expect(buttons[0].prop('children')).toBe('secondary action'); - expect(buttons[1].prop('children')).toBe('primary action'); - }); - - it('renders right-aligned if set to "right"', () => { - const card = mountWithApp( - -

Some card content.

-
, - ); - - const buttons = card.findAll(Button); - expect(buttons[0].prop('children')).toBe('secondary action'); - expect(buttons[1].prop('children')).toBe('primary action'); - }); - - it('renders left-aligned if set to "left"', () => { - const card = mountWithApp( - -

Some card content.

-
, - ); - - const buttons = card.findAll(Button); - expect(buttons[0].prop('children')).toBe('primary action'); - expect(buttons[1].prop('children')).toBe('secondary action'); - }); - }); - - describe('hideWhenPrinting prop', () => { - it('renders the classname "Card hideOnPrint" when passed', () => { - const card = mountWithApp( - -

Some card content.

-
, - ); - - expect(card).toContainReactComponent('div', { - className: 'Card hideOnPrint', - }); - }); - - it('does not render the classname "Card hideOnPrint" when prop is not passed', () => { - const card = mountWithApp( - -

Some card content.

-
, - ); - - expect(card).not.toContainReactComponent('div', { - className: 'Card hideOnPrint', - }); - }); + expect(card).toContainReactComponentTimes('p', 2); }); - it('renders a primary footer action', () => { + it('sets default border radius when roundedAbove breakpoint passed in', () => { + setMediaWidth('breakpoints-sm'); const card = mountWithApp( - -

Some card content.

+ + {heading} + {subheading} , ); - expect(card).toContainReactComponent(Button, {children: 'test action'}); - }); - - describe('secondaryFooterActions', () => { - it('renders a single secondary footer action button when only 1 is supplied', () => { - const card = mountWithApp( - -

Some card content.

-
, - ); - - expect(card).toContainReactComponent(Button, {children: 'test action'}); - expect(card).not.toContainReactComponent(Popover); - }); - - it('renders popover when >1 are supplied', () => { - const card = mountWithApp( - -

Some card content.

-
, - ); - - expect(card).toContainReactComponent(Button, { - children: 'More', - }); - expect(card).toContainReactComponent(Popover); - }); - - it('activates popover when disclosure button is clicked', () => { - const footerActions = [ - {content: 'Most important action'}, - {content: 'Second most important action'}, - ]; - const card = mountWithApp( - -

Some card content.

-
, - ); - const disclosureButton = card.findAll(Button)[0]; - expect(disclosureButton).toContainReactText('More'); - - expect(card).toContainReactComponent(Popover, { - active: false, - }); - - disclosureButton.trigger('onClick'); - - expect(card).toContainReactComponent(Popover, { - active: true, - }); - - expect(card).toContainReactComponent(ActionList, { - items: footerActions, - }); - }); - - it('sets the disclosure button content to the value set on secondaryFooterActionsDisclosureText', () => { - const card = mountWithApp( - -

Some card content.

-
, - ); - - expect(card).toContainReactComponent(Button, { - children: 'Show more', - }); + expect(card).toContainReactComponent('div', { + style: expect.objectContaining({ + '--pc-box-border-radius': 'var(--p-border-radius-2)', + }), }); }); - - it('renders a section when sectioned', () => { - const card = mountWithApp( - -

Some card content.

-
, - ); - - // eslint-disable-next-line import/no-deprecated - expect(card.find(Section)).toContainReactText('Some card content.'); - }); }); diff --git a/polaris-react/src/components/CheckableButton/CheckableButton.scss b/polaris-react/src/components/CheckableButton/CheckableButton.scss index dbd848c0111..74c9a2ad4df 100644 --- a/polaris-react/src/components/CheckableButton/CheckableButton.scss +++ b/polaris-react/src/components/CheckableButton/CheckableButton.scss @@ -6,8 +6,6 @@ font-size: var(--p-font-size-75); font-weight: var(--p-font-weight-medium); line-height: var(--p-font-line-height-1); - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - text-transform: initial; letter-spacing: initial; display: flex; align-items: center; diff --git a/polaris-react/src/components/Checkbox/Checkbox.tsx b/polaris-react/src/components/Checkbox/Checkbox.tsx index 9536074a7d6..a8640478895 100644 --- a/polaris-react/src/components/Checkbox/Checkbox.tsx +++ b/polaris-react/src/components/Checkbox/Checkbox.tsx @@ -3,12 +3,12 @@ import React, { useRef, useImperativeHandle, useContext, + useId, } from 'react'; import {MinusMinor, TickSmallMinor} from '@shopify/polaris-icons'; import {classNames} from '../../utilities/css'; import {useToggle} from '../../utilities/use-toggle'; -import {useUniqueId} from '../../utilities/unique-id'; import {Choice, helpTextID} from '../Choice'; import {errorTextID} from '../InlineError'; import {Icon} from '../Icon'; @@ -69,7 +69,8 @@ export const Checkbox = forwardRef( ref, ) { const inputNode = useRef(null); - const id = useUniqueId('Checkbox', idProp); + const uniqId = useId(); + const id = idProp ?? uniqId; const { value: mouseOver, setTrue: handleMouseOver, diff --git a/polaris-react/src/components/Checkbox/tests/Checkbox.test.tsx b/polaris-react/src/components/Checkbox/tests/Checkbox.test.tsx index 09de8ab1ab6..1688f8b21a7 100644 --- a/polaris-react/src/components/Checkbox/tests/Checkbox.test.tsx +++ b/polaris-react/src/components/Checkbox/tests/Checkbox.test.tsx @@ -103,7 +103,7 @@ describe('', () => { const element = mountWithApp(); expect(element).toContainReactComponent('input', { - id: 'PolarisCheckbox1', + id: ':r8:', }); }); }); @@ -139,7 +139,7 @@ describe('', () => { ); expect(checkbox).toContainReactComponent('input', { - 'aria-describedby': 'PolarisCheckbox1HelpText', + 'aria-describedby': ':rc:HelpText', }); expect(checkbox.find('div')).toContainReactText('Some help'); @@ -169,7 +169,7 @@ describe('', () => { ); expect(checkbox).toContainReactComponent('input', { - 'aria-describedby': 'PolarisCheckbox1Error', + 'aria-describedby': ':re:Error', }); expect(checkbox.find('div')).toContainReactText('Some error'); @@ -189,7 +189,7 @@ describe('', () => { ); expect(checkbox).toContainReactComponent('input', { - 'aria-describedby': 'PolarisCheckbox1Error PolarisCheckbox1HelpText', + 'aria-describedby': ':rg:Error :rg:HelpText', }); expect(checkbox.find('div')).toContainReactText('Some error'); expect(checkbox.find('div')).toContainReactText('Some help'); diff --git a/polaris-react/src/components/ChoiceList/ChoiceList.tsx b/polaris-react/src/components/ChoiceList/ChoiceList.tsx index 47a17c6cc71..afa790b85ac 100644 --- a/polaris-react/src/components/ChoiceList/ChoiceList.tsx +++ b/polaris-react/src/components/ChoiceList/ChoiceList.tsx @@ -1,6 +1,5 @@ -import React from 'react'; +import React, {useId} from 'react'; -import {useUniqueId} from '../../utilities/unique-id'; import type {Error} from '../../types'; import {Checkbox} from '../Checkbox'; import {RadioButton} from '../RadioButton'; @@ -64,7 +63,8 @@ export function ChoiceList({ // see https://github.com/Microsoft/TypeScript/issues/28768 const ControlComponent: any = allowMultiple ? Checkbox : RadioButton; - const name = useUniqueId('ChoiceList', nameProp); + const uniqName = useId(); + const name = nameProp ?? uniqName; const finalName = allowMultiple ? `${name}[]` : name; const titleMarkup = title ? ( diff --git a/polaris-react/src/components/Collapsible/Collapsible.tsx b/polaris-react/src/components/Collapsible/Collapsible.tsx index 8416172c196..618d5ed8c20 100644 --- a/polaris-react/src/components/Collapsible/Collapsible.tsx +++ b/polaris-react/src/components/Collapsible/Collapsible.tsx @@ -22,8 +22,6 @@ export interface CollapsibleProps { * @default transition={{duration: 'var(--p-motion-duration-150)', timingFunction: 'var(--p-motion-ease-in-out)'}} */ transition?: boolean | Transition; - /** @deprecated Re-measuring is no longer necessary on children update **/ - preventMeasuringOnChildrenUpdate?: boolean; /** Callback when the animation completes. */ onAnimationEnd?(): void; /** The content to display inside the collapsible. */ @@ -37,7 +35,6 @@ export function Collapsible({ expandOnPrint, open, transition = true, - preventMeasuringOnChildrenUpdate: _preventMeasuringOnChildrenUpdate, children, onAnimationEnd, }: CollapsibleProps) { diff --git a/polaris-react/src/components/Combobox/components/TextField/TextField.tsx b/polaris-react/src/components/Combobox/components/TextField/TextField.tsx index 894a948c58c..ad3fae7fdb7 100644 --- a/polaris-react/src/components/Combobox/components/TextField/TextField.tsx +++ b/polaris-react/src/components/Combobox/components/TextField/TextField.tsx @@ -1,7 +1,6 @@ -import React, {useMemo, useCallback, useEffect} from 'react'; +import React, {useMemo, useId, useCallback, useEffect} from 'react'; import {labelID} from '../../../Label'; -import {useUniqueId} from '../../../../utilities/unique-id'; import {TextField as PolarisTextField} from '../../../TextField'; import type {TextFieldProps} from '../../../TextField'; import {useComboboxTextField} from '../../../../utilities/combobox'; @@ -29,7 +28,7 @@ export function TextField({ onTextFieldBlur, } = comboboxTextFieldContext; - const uniqueId = useUniqueId('ComboboxTextField'); + const uniqueId = useId(); const textFieldId = useMemo(() => idProp || uniqueId, [uniqueId, idProp]); const labelId = useMemo( diff --git a/polaris-react/src/components/DisplayText/DisplayText.scss b/polaris-react/src/components/DisplayText/DisplayText.scss deleted file mode 100644 index cd394b37516..00000000000 --- a/polaris-react/src/components/DisplayText/DisplayText.scss +++ /dev/null @@ -1,55 +0,0 @@ -@import '../../styles/common'; - -.DisplayText { - margin: 0; -} - -.sizeSmall { - font-size: var(--p-font-size-200); - font-weight: var(--p-font-weight-regular); - line-height: var(--p-font-line-height-3); - - @media #{$p-breakpoints-md-up} { - font-size: var(--p-font-size-300); - line-height: var(--p-font-line-height-4); - } -} - -.sizeMedium { - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - font-size: 21px; - font-weight: var(--p-font-weight-regular); - line-height: var(--p-font-line-height-4); - - @media #{$p-breakpoints-md-up} { - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - font-size: 26px; - line-height: var(--p-font-line-height-5); - } -} - -.sizeLarge { - font-size: var(--p-font-size-400); - font-weight: var(--p-font-weight-semibold); - line-height: var(--p-font-line-height-4); - - @media #{$p-breakpoints-md-up} { - font-size: var(--p-font-size-500); - line-height: var(--p-font-line-height-5); - } -} - -.sizeExtraLarge { - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - font-size: 27px; - font-weight: var(--p-font-weight-semibold); - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - line-height: 36px; - - @media #{$p-breakpoints-md-up} { - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - font-size: 42px; - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - line-height: 44px; - } -} diff --git a/polaris-react/src/components/DisplayText/DisplayText.stories.tsx b/polaris-react/src/components/DisplayText/DisplayText.stories.tsx deleted file mode 100644 index c8d7b767587..00000000000 --- a/polaris-react/src/components/DisplayText/DisplayText.stories.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; -import type {ComponentMeta} from '@storybook/react'; -import {DisplayText} from '@shopify/polaris'; - -export default { - component: DisplayText, -} as ComponentMeta; - -export function ExtraLarge() { - return Good evening, Dominic.; -} - -export function Large() { - return Good evening, Dominic.; -} - -export function Medium() { - return Good evening, Dominic.; -} - -export function Small() { - return Good evening, Dominic.; -} diff --git a/polaris-react/src/components/DisplayText/DisplayText.tsx b/polaris-react/src/components/DisplayText/DisplayText.tsx deleted file mode 100644 index f0b96464b83..00000000000 --- a/polaris-react/src/components/DisplayText/DisplayText.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react'; - -import {classNames, variationName} from '../../utilities/css'; -import type {HeadingTagName} from '../../types'; - -import styles from './DisplayText.scss'; - -type Size = 'small' | 'medium' | 'large' | 'extraLarge'; - -export interface DisplayTextProps { - /** - * Name of element to use for text - * @default 'p' - */ - element?: HeadingTagName; - /** - * Size of the text - * @default 'medium' - */ - size?: Size; - /** Content to display */ - children?: React.ReactNode; -} - -/** - * @deprecated The DisplayText component will be removed in the next - * major version. Use the Text component instead. See the - * Polaris component guide on how to use Text. - * - * https://polaris.shopify.com/components/text - */ -export function DisplayText({ - element: Element = 'p', - children, - size = 'medium', -}: DisplayTextProps) { - const className = classNames( - styles.DisplayText, - size && styles[variationName('size', size)], - ); - - return {children}; -} diff --git a/polaris-react/src/components/DisplayText/index.ts b/polaris-react/src/components/DisplayText/index.ts deleted file mode 100644 index 1688b1c39f6..00000000000 --- a/polaris-react/src/components/DisplayText/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './DisplayText'; diff --git a/polaris-react/src/components/DisplayText/tests/DisplayText.test.tsx b/polaris-react/src/components/DisplayText/tests/DisplayText.test.tsx deleted file mode 100644 index e1fc5169c88..00000000000 --- a/polaris-react/src/components/DisplayText/tests/DisplayText.test.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; -import {mountWithApp} from 'tests/utilities'; - -// eslint-disable-next-line import/no-deprecated -import {DisplayText} from '../DisplayText'; - -describe('', () => { - it('renders its children', () => { - const text = 'Important text.'; - const displayText = mountWithApp( - - {text} - , - ); - expect(displayText).toContainReactText(text); - }); - - it('renders the specified element', () => { - const displayText = mountWithApp( - - Important text. - , - ); - expect(displayText).toContainReactComponentTimes('h1', 1); - }); - - it('renders a p element if not specified', () => { - const displayText = mountWithApp( - Important text., - ); - - expect(displayText).toContainReactComponentTimes('p', 1); - }); -}); diff --git a/polaris-react/src/components/DropZone/DropZone.stories.tsx b/polaris-react/src/components/DropZone/DropZone.stories.tsx index 7a1e366e2d3..367685cb9e3 100644 --- a/polaris-react/src/components/DropZone/DropZone.stories.tsx +++ b/polaris-react/src/components/DropZone/DropZone.stories.tsx @@ -39,8 +39,7 @@ export function Default() { alt={file.name} source={ validImageTypes.includes(file.type) - ? // eslint-disable-next-line node/no-unsupported-features/node-builtins - window.URL.createObjectURL(file) + ? window.URL.createObjectURL(file) : NoteMinor } /> @@ -93,7 +92,6 @@ export function WithImageFileUpload() {
@@ -152,8 +150,7 @@ export function WithSingleFileUpload() { alt={file.name} source={ validImageTypes.includes(file.type) - ? // eslint-disable-next-line node/no-unsupported-features/node-builtins - window.URL.createObjectURL(file) + ? window.URL.createObjectURL(file) : NoteMinor } /> @@ -194,8 +191,7 @@ export function WithDropOnPage() { alt={file.name} source={ validImageTypes.includes(file.type) - ? // eslint-disable-next-line node/no-unsupported-features/node-builtins - window.URL.createObjectURL(file) + ? window.URL.createObjectURL(file) : NoteMinor } /> @@ -214,7 +210,7 @@ export function WithDropOnPage() { return (
@@ -319,8 +314,7 @@ export function Nested() { alt={file.name} source={ validImageTypes.includes(file.type) - ? // eslint-disable-next-line node/no-unsupported-features/node-builtins - window.URL.createObjectURL(file) + ? window.URL.createObjectURL(file) : NoteMinor } /> @@ -391,8 +385,7 @@ export function WithCustomFileUploadText() { alt={file.name} source={ validImageTypes.includes(file.type) - ? // eslint-disable-next-line node/no-unsupported-features/node-builtins - window.URL.createObjectURL(file) + ? window.URL.createObjectURL(file) : NoteMinor } /> @@ -440,8 +433,7 @@ export function WithCustomFileDialogTrigger() { alt={file.name} source={ validImageTypes.includes(file.type) - ? // eslint-disable-next-line node/no-unsupported-features/node-builtins - window.URL.createObjectURL(file) + ? window.URL.createObjectURL(file) : NoteMinor } /> diff --git a/polaris-react/src/components/DropZone/DropZone.tsx b/polaris-react/src/components/DropZone/DropZone.tsx index 1a41e8325c3..aa889e70dce 100755 --- a/polaris-react/src/components/DropZone/DropZone.tsx +++ b/polaris-react/src/components/DropZone/DropZone.tsx @@ -1,4 +1,11 @@ -import React, {useState, useRef, useCallback, useMemo, useEffect} from 'react'; +import React, { + useState, + useRef, + useCallback, + useMemo, + useEffect, + useId, +} from 'react'; import type {FunctionComponent} from 'react'; import {UploadMajor, CircleAlertMajor} from '@shopify/polaris-icons'; @@ -11,7 +18,6 @@ import {Labelled} from '../Labelled'; import type {LabelledProps} from '../Labelled'; import {useI18n} from '../../utilities/i18n'; import {isServer} from '../../utilities/target'; -import {useUniqueId} from '../../utilities/unique-id'; import {useComponentDidMount} from '../../utilities/use-component-did-mount'; import {useToggle} from '../../utilities/use-toggle'; import {VerticalStack} from '../VerticalStack'; @@ -294,7 +300,8 @@ export const DropZone: React.FunctionComponent & { adjustSize(); }); - const id = useUniqueId('DropZone', idProp); + const uniqId = useId(); + const id = idProp ?? uniqId; const typeSuffix = capitalize(type); const allowMultipleKey = createAllowMultipleKey(allowMultiple); diff --git a/polaris-react/src/components/Filters/Filters.scss b/polaris-react/src/components/Filters/Filters.scss index e7d1e5a5b89..2bbcf49169f 100644 --- a/polaris-react/src/components/Filters/Filters.scss +++ b/polaris-react/src/components/Filters/Filters.scss @@ -1,227 +1,235 @@ @import '../../styles/common'; -$list-filters-header-height: $top-bar-height; -$list-filters-footer-height: 70px; - -.Filters { +.Container { position: relative; + z-index: var(--p-z-index-1); + border-bottom: var(--p-border-width-1) solid var(--p-color-border-subdued); + border-top-left-radius: var(--p-border-radius-2); + border-top-right-radius: var(--p-border-radius-2); + background: var(--p-color-bg); } -.FiltersContainer { - position: relative; - height: 100%; - width: 100%; - display: flex; - flex-direction: column; +@media #{$p-breakpoints-sm-down} { + .Container { + border-top-left-radius: 0; + border-top-right-radius: 0; + height: 57px; + } } -.FiltersContainerHeader { - top: 0; - width: 100%; - padding: var(--p-space-4) var(--p-space-5); - border-bottom: var(--p-border-width-1) solid var(--p-color-border-subdued); - height: $list-filters-header-height; - box-sizing: border-box; - display: flex; - align-items: center; - justify-content: space-between; +.SearchField { + flex: 1; } -.FiltersDesktopContainerContent { - width: 100%; - height: calc( - 100% - #{$list-filters-footer-height} - #{$list-filters-header-height} - ); - padding: var(--p-space-2); +.Spinner { + width: var(--p-space-5); + transform: translateX(var(--p-space-1)); + + svg { + display: block; + } } -.FiltersMobileContainerContent { - width: 100%; - height: calc(100% - #{$list-filters-header-height}); - padding: var(--p-space-2); +.FiltersWrapper { + border-bottom: var(--p-border-width-1) solid var(--p-color-border-subdued); + height: 53px; + overflow: hidden; + + @media #{$p-breakpoints-sm-down} { + background: var(--p-color-bg); + } + + @media #{$p-breakpoints-md-up} { + height: auto; + overflow: visible; + } } -.FiltersContainerFooter { - position: absolute; - bottom: 0; - width: 100%; - padding: var(--p-space-4) var(--p-space-5); - border-top: var(--p-border-width-1) solid var(--p-color-border-subdued); - height: $list-filters-footer-height; - box-sizing: border-box; +.hideQueryField .FiltersWrapper { display: flex; align-items: center; - justify-content: space-between; } -.FiltersMobileContainerFooter { - width: 100%; - padding: var(--p-space-4) var(--p-space-4); - height: $list-filters-footer-height; - box-sizing: border-box; - display: flex; - align-items: center; - justify-content: space-between; +.FiltersInner { + overflow: auto; + white-space: nowrap; + padding: var(--p-space-3) var(--p-space-4) var(--p-space-5); } -.EmptyFooterState { - border-top: var(--p-border-width-1) solid var(--p-color-border-subdued); - padding-top: var(--p-space-4); - width: 100%; - display: flex; - justify-content: center; +.hideQueryField .FiltersInner { + flex: 1; } -.FilterTriggerContainer { - position: relative; +@media #{$p-breakpoints-md-up} { + .FiltersInner { + overflow: visible; + flex-wrap: wrap; + gap: var(--p-space-2); + padding: calc(var(--p-space-2) - var(--p-space-05)) var(--p-space-2); + } } -.FilterTrigger { - width: 100%; - margin: 0; - padding: var(--p-space-4) var(--p-space-5); +.AddFilter { + background: var(--p-color-bg-subdued); + border-radius: var(--p-border-radius-6); + border: var(--p-color-border-subdued) dashed var(--p-border-width-1); + padding: 0 var(--p-space-2) 0 var(--p-space-3); + height: 28px; + cursor: pointer; color: var(--p-color-text); - border-radius: var(--p-border-radius-1); - background: none; - border: none; - outline: none; + display: flex; + align-items: center; + justify-content: center; + font-size: var(--p-font-size-100); + font-family: var(--p-font-family-sans); + line-height: var(--p-font-line-height-2); + outline: inherit; // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - @include focus-ring; + @include focus-ring($border-width: var(--p-border-width-1)); - &:focus { - box-shadow: none; + path { + fill: var(--p-color-icon); } - &:hover { - cursor: pointer; - background-color: var(--p-color-bg-hover); - outline: var(--p-border-width-1) solid transparent; - } - - &:focus-visible:not(:active) { + @media #{$p-breakpoints-md-up} { + font-size: var(--p-font-size-75); + line-height: var(--p-font-line-height-1); + height: 24px; // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - @include focus-ring($style: 'focused'); + padding: 0 6px 0 var(--p-space-2); } -} -.FilterTriggerTitle { - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - font-size: 15px; - font-weight: var(--p-font-weight-semibold); -} + &:hover, + &:focus { + background: var(--p-color-bg-hover); + border-color: var(--p-color-border-hover); -.AppliedFilterBadgeContainer { - padding-top: var(--p-space-1); - display: flex; + path { + fill: var(--p-color-icon-hover); + } + } - .open & { - display: none; + &:active { + background: var(--p-color-bg-active); + border-color: var(--p-color-border-hover); } -} -.FilterTriggerLabelContainer { - display: flex; - align-items: center; - justify-content: space-between; -} + &[aria-disabled='true'] { + background: var(--p-color-bg-disabled); + border-color: var(--p-color-border-disabled); + color: var(--p-color-text-disabled); + cursor: default; -.open { - &::before, - &::after { - content: ''; - position: relative; - left: var(--p-space-4); - width: calc(100% - var(--p-space-8)); - height: var(--p-space-025); - background-color: var(--p-color-bg-subdued); - display: block; + path { + fill: var(--p-color-icon-disabled); + } } - &::before { - top: 0; + &:focus-visible:not(:active) { + // stylelint-disable-next-line -- no way to set focus ring without mixin currently + @include focus-ring($style: 'focused'); } &::after { - bottom: 0; + border-radius: var(--p-border-radius-6); } -} -.open.first { - &::after { - content: ''; - bottom: 0; - position: relative; - left: var(--p-space-4); - width: calc(100% - var(--p-space-8)); - height: var(--p-space-025); - background-color: var(--p-color-bg-subdued); - display: block; - } + span { + margin-right: var(--p-space-05); - &::before { - display: none; + @media #{$p-breakpoints-md-up} { + margin-right: var(--p-space-025); + } } -} -.open ~ .open { - &::before { - display: none; + svg { + width: var(--p-space-5); + + @media #{$p-breakpoints-md-up} { + width: var(--p-space-4); + } } } -.open.last { - &::before { - content: ''; - top: 0; +@media #{$p-breakpoints-md-down} { + .FiltersWrapperWithAddButton { position: relative; - left: var(--p-space-4); - width: calc(100% - var(--p-space-8)); - height: var(--p-space-025); - background-color: var(--p-color-bg-subdued); - display: block; - } - &::after { - display: none; + .FiltersInner { + padding-top: var(--p-space-2); + padding-bottom: var(--p-space-5); + padding-right: 0; + } } -} -.open + .last { - &::before { - display: none; + .AddFilterActivatorMultiple { + position: sticky; + z-index: var(--p-z-index-1); + top: 0; + right: 0; + display: flex; + padding: var(--p-space-1) var(--p-space-4) var(--p-space-1) 0; + background: var(--p-color-bg); + margin-left: var(--p-space-2); + + &::before { + content: ''; + position: absolute; + top: 0; + left: -12px; + width: 12px; + height: 100%; + pointer-events: none; + // stylelint-disable -- needed to create the fade effect + background: linear-gradient( + 90deg, + rgba(255, 255, 255, 0) 0%, + var(--p-color-bg) 70%, + var(--p-color-bg) 100% + ); + // stylelint-enable + } + + .AddFilter { + // stylelint-disable-next-line -- hardcoded value required for responsive design + padding: 0.75rem var(--p-space-2); + + // stylelint-disable-next-line selector-max-combinators -- required to hide the text of the button + span { + display: none; + } + } } } -.FilterNodeContainer { - padding: var(--p-space-2) var(--p-space-5) var(--p-space-5) var(--p-space-5); -} - -.SearchIcon { - fill: currentColor; -} +.FiltersStickyArea { + position: relative; + display: flex; + gap: var(--p-space-1); + flex-wrap: nowrap; + align-items: center; + justify-content: flex-start; -.Backdrop { - position: fixed; - z-index: var(--p-z-index-10); - top: 0; - right: 0; - bottom: 0; - left: 0; - display: block; - opacity: 0; + @media #{$p-breakpoints-md-up} { + flex-wrap: wrap; + } } -.HelpText { - margin-top: var(--p-space-2); +.ClearAll { + margin-left: var(--p-space-1); } -.TagsContainer { - display: flex; - padding-top: var(--p-space-2); - flex-wrap: wrap; +@media #{$p-breakpoints-md-down} { + .ClearAll { + margin-left: var(--p-space-2); + padding-right: var(--p-space-4); + } - > * { - margin-right: var(--p-space-2); - margin-bottom: var(--p-space-2); + .MultiplePinnedFilterClearAll { + transform: translateX(-8px); + position: relative; + z-index: var(--p-z-index-1); + margin-left: 0; + padding-right: var(--p-space-4); } } diff --git a/polaris-react/src/components/Filters/Filters.stories.tsx b/polaris-react/src/components/Filters/Filters.stories.tsx index 86730115a44..3577e278f8d 100644 --- a/polaris-react/src/components/Filters/Filters.stories.tsx +++ b/polaris-react/src/components/Filters/Filters.stories.tsx @@ -87,6 +87,7 @@ export function WithAResourceList() { /> ), shortcut: true, + pinned: true, }, { key: 'taggedWith', @@ -101,6 +102,7 @@ export function WithAResourceList() { /> ), shortcut: true, + pinned: true, }, { key: 'moneySpent', @@ -155,6 +157,7 @@ export function WithAResourceList() { filterControl={ } + flushFilters items={[ { id: 341, @@ -341,16 +345,15 @@ export function WithADataTable() { return (
- - - +
-
} + flushFilters items={[ { id: 341, @@ -583,6 +593,7 @@ export function Disabled() { filterControl={
} + flushFilters items={[ { id: 341, @@ -730,6 +745,7 @@ export function SomeDisabled() { filterControl={
} + flushFilters items={[ { id: 341, @@ -801,135 +821,7 @@ export function SomeDisabled() { } } -export function WithoutClearButton() { - const [taggedWith, setTaggedWith] = useState(null); - const [queryValue, setQueryValue] = useState(null); - - const handleTaggedWithChange = useCallback( - (value) => setTaggedWith(value), - [], - ); - const handleQueryValueChange = useCallback( - (value) => setQueryValue(value), - [], - ); - - const handleTaggedWithRemove = useCallback(() => setTaggedWith(null), []); - const handleQueryValueRemove = useCallback(() => setQueryValue(null), []); - - const handleClearAll = useCallback(() => { - handleTaggedWithRemove(); - handleQueryValueRemove(); - }, [handleQueryValueRemove, handleTaggedWithRemove]); - - const filters = [ - { - key: 'taggedWith', - label: 'Tagged with', - filter: ( - - ), - shortcut: true, - hideClearButton: true, - }, - ]; - - const appliedFilters = !isEmpty(taggedWith) - ? [ - { - key: 'taggedWith', - label: disambiguateLabel('taggedWith', taggedWith), - onRemove: handleTaggedWithRemove, - }, - ] - : []; - - return ( -
- - -
- -
- - } - items={[ - { - id: 341, - url: '#', - name: 'Mae Jemison', - location: 'Decatur, USA', - }, - { - id: 256, - url: '#', - name: 'Ellen Ochoa', - location: 'Los Angeles, USA', - }, - ]} - renderItem={(item) => { - const {id, url, name, location} = item; - const media = ; - - return ( - - - {name} - -
{location}
-
- ); - }} - /> -
-
- ); - - function disambiguateLabel(key, value) { - switch (key) { - case 'taggedWith': - return `Tagged with ${value}`; - default: - return value; - } - } - - function isEmpty(value) { - if (Array.isArray(value)) { - return value.length === 0; - } else { - return value === '' || value == null; - } - } -} - -export function WithHelpText() { +export function WithQueryFieldHidden() { const [accountStatus, setAccountStatus] = useState(null); const [moneySpent, setMoneySpent] = useState(null); const [taggedWith, setTaggedWith] = useState(null); @@ -1058,15 +950,25 @@ export function WithHelpText() { filterControl={ + hideQueryField + > + + } + flushFilters items={[ { id: 341, @@ -1126,7 +1028,7 @@ export function WithHelpText() { } } -export function WithQueryFieldHidden() { +export function WithQueryFieldDisabled() { const [accountStatus, setAccountStatus] = useState(null); const [moneySpent, setMoneySpent] = useState(null); const [taggedWith, setTaggedWith] = useState(null); @@ -1255,14 +1157,16 @@ export function WithQueryFieldHidden() { filterControl={ } + flushFilters items={[ { id: 341, @@ -1322,8 +1226,9 @@ export function WithQueryFieldHidden() { } } -export function WithQueryFieldDisabled() { +export function WithAdditionalFilterSections() { const [accountStatus, setAccountStatus] = useState(null); + const [accountId, setAccountId] = useState(null); const [moneySpent, setMoneySpent] = useState(null); const [taggedWith, setTaggedWith] = useState(null); const [queryValue, setQueryValue] = useState(null); @@ -1332,6 +1237,7 @@ export function WithQueryFieldDisabled() { (value) => setAccountStatus(value), [], ); + const handleAccountIdChange = useCallback((value) => setAccountId(value), []); const handleMoneySpentChange = useCallback( (value) => setMoneySpent(value), [], @@ -1348,25 +1254,42 @@ export function WithQueryFieldDisabled() { () => setAccountStatus(null), [], ); + const handleAccountIdRemove = useCallback(() => setAccountId(null), []); const handleMoneySpentRemove = useCallback(() => setMoneySpent(null), []); const handleTaggedWithRemove = useCallback(() => setTaggedWith(null), []); const handleQueryValueRemove = useCallback(() => setQueryValue(null), []); const handleFiltersClearAll = useCallback(() => { handleAccountStatusRemove(); + handleAccountIdRemove(); handleMoneySpentRemove(); handleTaggedWithRemove(); handleQueryValueRemove(); }, [ handleAccountStatusRemove, + handleAccountIdRemove, handleMoneySpentRemove, handleQueryValueRemove, handleTaggedWithRemove, ]); const filters = [ + { + key: 'taggedWith', + label: 'Tagged with', + filter: ( + + ), + }, { key: 'accountStatus', label: 'Account status', + section: 'Account filters', filter: ( ), - shortcut: true, }, { - key: 'taggedWith', - label: 'Tagged with', + key: 'accountId', + label: 'Account ID', + section: 'Account filters', filter: ( ), - shortcut: true, }, { key: 'moneySpent', label: 'Money spent', + section: 'Money filters', filter: ( } + flushFilters items={[ { id: 341, @@ -1504,6 +1436,8 @@ export function WithQueryFieldDisabled() { return `Tagged with ${value}`; case 'accountStatus': return value.map((val) => `Customer ${val}`).join(', '); + case 'accountId': + return `Account id: ${value}`; default: return value; } diff --git a/polaris-react/src/components/Filters/Filters.tsx b/polaris-react/src/components/Filters/Filters.tsx index d6c45116361..381af3cd210 100644 --- a/polaris-react/src/components/Filters/Filters.tsx +++ b/polaris-react/src/components/Filters/Filters.tsx @@ -1,602 +1,444 @@ -import React, {Component, createRef} from 'react'; -import { - SearchMinor, - ChevronUpMinor, - ChevronDownMinor, - CancelSmallMinor, -} from '@shopify/polaris-icons'; +import React, {useState, useRef, useEffect, useMemo} from 'react'; +import type {ReactNode} from 'react'; +import {PlusMinor} from '@shopify/polaris-icons'; +import type {TransitionStatus} from 'react-transition-group'; -import {classNames} from '../../utilities/css'; -import {ResourceListContext} from '../../utilities/resource-list'; import {useI18n} from '../../utilities/i18n'; -import {useMediaQuery} from '../../utilities/media-query'; -import {focusFirstFocusableNode} from '../../utilities/focus'; -import {WithinFilterContext} from '../../utilities/within-filter-context'; -import {Button} from '../Button'; +import {Popover} from '../Popover'; +import {ActionList} from '../ActionList'; import {Text} from '../Text'; -import {Collapsible} from '../Collapsible'; -import {Scrollable} from '../Scrollable'; -import {ScrollLock} from '../ScrollLock'; -import {Icon} from '../Icon'; -import {TextField} from '../TextField'; -import {Tag} from '../Tag'; -import {Badge} from '../Badge'; -import {Focus} from '../Focus'; -// eslint-disable-next-line import/no-deprecated -import {Sheet} from '../Sheet'; -import {LegacyStack} from '../LegacyStack'; -import {Key} from '../../types'; -import {KeypressListener} from '../KeypressListener'; - -import {ConnectedFilterControl, TagsWrapper} from './components'; -import type {ConnectedFilterControlProps} from './components'; +import {UnstyledButton} from '../UnstyledButton'; +import {classNames} from '../../utilities/css'; +import type { + ActionListItemDescriptor, + AppliedFilterInterface, + FilterInterface, +} from '../../types'; +import {HorizontalStack} from '../HorizontalStack'; +import {Box} from '../Box'; +import {Spinner} from '../Spinner'; +import {Button} from '../Button'; + +import {FilterPill, SearchField} from './components'; import styles from './Filters.scss'; -export interface AppliedFilterInterface { - /** A unique key used to identify the applied filter */ - key: string; - /** A label for the applied filter */ - label: string; - /** Callback when the remove button is pressed */ - onRemove(key: string): void; -} +const TRANSITION_DURATION = 'var(--p-motion-duration-150)'; +const TRANSITION_MARGIN = '-36px'; -export interface FilterInterface { - /** A unique key used to identify the filter */ - key: string; - /** The label for the filter */ - label: string; - /** The markup for the given filter */ - filter: React.ReactNode; - /** Whether or not the filter should have a shortcut popover displayed */ - shortcut?: boolean; - /** Whether or not the filter is disabled */ - disabled?: boolean; - /** - * @default false - * Whether or not the clear button is displayed - */ - hideClearButton?: boolean; -} +const defaultStyle = { + transition: `opacity ${TRANSITION_DURATION} var(--p-motion-ease)`, + opacity: 0, +}; + +const transitionStyles = { + entering: {opacity: 1}, + entered: {opacity: 1}, + exiting: {opacity: 0}, + exited: {opacity: 0}, + unmounted: {opacity: 0}, +}; + +const defaultFilterStyles = { + transition: `opacity ${TRANSITION_DURATION} var(--p-motion-ease), margin ${TRANSITION_DURATION} var(--p-motion-ease)`, + opacity: 0, + marginTop: TRANSITION_MARGIN, +}; + +const transitionFilterStyles = { + entering: { + opacity: 1, + marginTop: 0, + }, + entered: { + opacity: 1, + marginTop: 0, + }, + exiting: { + opacity: 0, + marginTop: TRANSITION_MARGIN, + }, + exited: { + opacity: 0, + marginTop: TRANSITION_MARGIN, + }, + unmounted: { + opacity: 0, + marginTop: TRANSITION_MARGIN, + }, +}; export interface FiltersProps { /** Currently entered text in the query field */ queryValue?: string; - /** Placeholder text for the query field */ + /** Placeholder text for the query field. */ queryPlaceholder?: string; - /** Whether the query field is focused */ + /** Whether the query field is focused. */ focused?: boolean; - /** Available filters added to the sheet. Shortcut filters are exposed outside of the sheet. */ + /** Available filters added to the filter bar. Shortcut filters are pinned to the front of the bar. */ filters: FilterInterface[]; - /** Applied filters which are rendered as tags. The remove callback is called with the respective key */ + /** Applied filters which are rendered as filter pills. The remove callback is called with the respective key. */ appliedFilters?: AppliedFilterInterface[]; - /** Callback when the query field is changed */ - onQueryChange(queryValue: string): void; - /** Callback when the clear button is triggered */ - onQueryClear(): void; - /** Callback when the reset all button is pressed */ - onClearAll(): void; - /** Callback when the query field is blurred */ - onQueryBlur?(): void; - /** Callback when the query field is focused */ - onQueryFocus?(): void; - /** The content to display inline with the controls */ - children?: React.ReactNode; - /** Disable all filters */ + /** Callback when the query field is changed. */ + onQueryChange: (queryValue: string) => void; + /** Callback when the clear button is triggered. */ + onQueryClear: () => void; + /** Callback when the reset all button is pressed. */ + onClearAll: () => void; + /** Callback when the query field is blurred. */ + onQueryBlur?: () => void; + /** Callback when the query field is focused. */ + onQueryFocus?: () => void; + /** The content to display inline with the controls. */ + children?: ReactNode; + /** Disable all filters. */ disabled?: boolean; - /** Additional hint text to display below the filters */ - helpText?: string | React.ReactNode; - /** Hide tags for applied filters */ - hideTags?: boolean; - /** Hide the query field */ + /** Hide filter bar for applied filters. */ + hideFilters?: boolean; + /** Hide the query field. */ hideQueryField?: boolean; - /** Disable the query field */ + /** Disable the query field. */ disableQueryField?: boolean; + /** Disable the filters */ + disableFilters?: boolean; + /** Whether the text field should be borderless. Should be true when used as part of the IndexFilters component. */ + borderlessQueryField?: boolean; + /** Whether an asyncronous task is currently being run. */ + loading?: boolean; + mountedState?: TransitionStatus; + /** Callback when the add filter button is clicked. */ + onAddFilterClick?: () => void; } -type CombinedProps = FiltersProps & { - i18n: ReturnType; - mediaQuery: ReturnType; -}; - -interface State { - open: boolean; - readyForFocus: boolean; - [key: string]: boolean; -} +export function Filters({ + queryValue, + queryPlaceholder, + focused, + filters, + appliedFilters, + onQueryChange, + onQueryClear, + onQueryBlur, + onQueryFocus, + onClearAll, + children, + disabled, + hideFilters, + hideQueryField, + disableQueryField, + borderlessQueryField, + loading, + disableFilters, + mountedState, + onAddFilterClick, +}: FiltersProps) { + const i18n = useI18n(); + const [popoverActive, setPopoverActive] = useState(false); + const [localPinnedFilters, setLocalPinnedFilters] = useState([]); + const hasMounted = useRef(false); -enum Suffix { - Filter = 'Filter', - Shortcut = 'Shortcut', -} + useEffect(() => { + hasMounted.current = true; + }); -class FiltersInner extends Component { - static contextType = ResourceListContext; - context!: React.ContextType; + const togglePopoverActive = () => + setPopoverActive((popoverActive) => !popoverActive); - state: State = { - open: false, - readyForFocus: false, + const handleAddFilterClick = () => { + onAddFilterClick?.(); + togglePopoverActive(); }; + const appliedFilterKeys = appliedFilters?.map(({key}) => key); + + const pinnedFiltersFromPropsAndAppliedFilters = filters.filter( + ({pinned, key}) => + (Boolean(pinned) || appliedFilterKeys?.includes(key)) && + // Filters that are pinned in local state display at the end of our list + !localPinnedFilters.find((filterKey) => filterKey === key), + ); + const pinnedFiltersFromLocalState = localPinnedFilters + .map((key) => filters.find((filter) => filter.key === key)) + .reduce( + (acc, filter) => (filter ? [...acc, filter] : acc), + [], + ); - private moreFiltersButtonContainer = createRef(); - private moreFiltersDoneButtonContainer = createRef(); - private focusNode = createRef(); - - render() { - const { - filters, - queryValue, - onQueryBlur, - onQueryChange, - onQueryFocus, - focused, - onClearAll, - appliedFilters, - onQueryClear, - queryPlaceholder, - children, - disabled = false, - helpText, - hideTags, - hideQueryField, - disableQueryField = false, - i18n, - mediaQuery: {isNavigationCollapsed}, - } = this.props; - const {resourceName} = this.context; - const {open, readyForFocus} = this.state; - - const backdropMarkup = open ? ( - <> - -
- - ) : null; - - const filtersContentMarkup = filters.map((filter, index) => { - const filterIsOpen = this.state[`${filter.key}${Suffix.Filter}`] === true; - const icon = filterIsOpen ? ChevronUpMinor : ChevronDownMinor; - const className = classNames( - styles.FilterTriggerContainer, - filterIsOpen && styles.open, - index === 0 && styles.first, - filters.length !== 1 && index === filters.length - 1 && styles.last, - ); + const pinnedFilters = [ + ...pinnedFiltersFromPropsAndAppliedFilters, + ...pinnedFiltersFromLocalState, + ]; + + const onFilterClick = + ({key, onAction}: FilterInterface) => + () => { + // PopoverOverlay will cause a rerender of the component and nuke the + // popoverActive state, so we set this as a microtask + setTimeout(() => { + setLocalPinnedFilters((currentLocalPinnedFilters) => [ + ...new Set([...currentLocalPinnedFilters, key]), + ]); + onAction?.(); + togglePopoverActive(); + }, 0); + }; - const appliedFilterContent = this.getAppliedFilterContent(filter.key); - const appliedFilterBadgeMarkup = appliedFilterContent ? ( -
- {appliedFilterContent} -
- ) : null; + const filterToActionItem = (filter: FilterInterface) => ({ + ...filter, + content: filter.label, + onAction: onFilterClick(filter), + }); + + const unpinnedFilters = filters.filter( + (filter) => !pinnedFilters.some(({key}) => key === filter.key), + ); + + const unsectionedFilters = unpinnedFilters + .filter((filter) => !filter.section) + .map(filterToActionItem); + + const sectionedFilters = unpinnedFilters + .filter((filter) => filter.section) + .reduce( + (acc, filter) => { + const filterActionItem = filterToActionItem(filter); + const sectionIndex = acc.findIndex( + (section) => section.title === filter.section, + ); + + if (sectionIndex === -1) { + acc.push({ + title: filter.section!, + items: [filterActionItem], + }); + } else { + acc[sectionIndex].items.push(filterActionItem); + } + + return acc; + }, + [] as { + title: string; + items: ActionListItemDescriptor[]; + }[], + ); - const collapsibleID = `${filter.key}Collapsible`; + const hasOneOrMorePinnedFilters = pinnedFilters.length >= 1; + + const addFilterActivator = ( +
+ + + {i18n.translate('Polaris.Filters.addFilter')} + + + +
+ ); + + const handleClearAllFilters = () => { + setLocalPinnedFilters([]); + onClearAll?.(); + }; - const buttonClassName = classNames(styles.FilterTrigger); + const shouldShowAddButton = filters.some((filter) => !filter.pinned); - return ( -
- - -
- - {this.generateFilterMarkup(filter)} - -
-
+ const additionalContent = useMemo(() => { + return ( + <> +
+ {loading ? : null}
- ); - }); - - const appliedFiltersCount = appliedFilters ? appliedFilters.length : 0; - const moreFiltersLabel = - hideTags && appliedFiltersCount > 0 - ? i18n.translate('Polaris.Filters.moreFiltersWithCount', { - count: appliedFiltersCount, - }) - : i18n.translate('Polaris.Filters.moreFilters'); - - const rightActionMarkup = filters.length ? ( -
- -
- ) : null; - - const filterResourceName = resourceName || { - singular: i18n.translate('Polaris.ResourceList.defaultItemSingular'), - plural: i18n.translate('Polaris.ResourceList.defaultItemPlural'), - }; - - const transformedFilters = this.transformFilters(filters); - - const filtersControlMarkup = ( - transformedFilters.length} - queryFieldHidden={hideQueryField} + {children} + + ); + }, [loading, children]); + + const queryFieldMarkup = hideQueryField ? null : ( +
+ - {hideQueryField ? null : ( - - - + +
- )} - - ); - - const filtersContainerHeaderClassname = classNames( - styles.FiltersContainerHeader, - ); + > + +
+ {additionalContent} +
+
+
+ ); + + const mountedStateStyles = + mountedState && !hideQueryField + ? { + ...defaultFilterStyles, + ...transitionFilterStyles[mountedState], + } + : undefined; + + const pinnedFiltersMarkup = pinnedFilters.map( + ({key: filterKey, ...pinnedFilter}) => { + const appliedFilter = appliedFilters?.find(({key}) => key === filterKey); + const handleFilterPillRemove = () => { + setLocalPinnedFilters((currentLocalPinnedFilters) => + currentLocalPinnedFilters.filter((key) => key !== filterKey), + ); + appliedFilter?.onRemove(filterKey); + }; - const filtersDesktopHeaderMarkup = ( -
- - {moreFiltersLabel} - -
- ); - - const filtersMobileHeaderMarkup = ( -
- -
- ); - - const filtersDesktopFooterClassname = classNames( - styles.FiltersContainerFooter, - ); - - const filtersDesktopFooterMarkup = ( -
+ +
+ ) : null; + + const clearAllMarkup = + appliedFilters?.length || localPinnedFilters.length ? ( +
-
- -
-
- ); - - const filtersMobileFooterMarkup = ( -
- {this.hasAppliedFilters() ? ( - - ) : ( -
- -

{i18n.translate('Polaris.Filters.noFiltersApplied')}

-
-
- )}
- ); - - const shouldHideTagsContainer = - !appliedFilters || appliedFilters.length < 1; - const tagsMarkup = !hideTags ? ( - ) : null; - const filtersMobileContainerContentClassName = classNames( - styles.FiltersMobileContainerContent, - ); - - const filtersDesktopContainerContentClassName = classNames( - styles.FiltersDesktopContainerContent, - ); - - const filtersContainerMarkup = isNavigationCollapsed ? ( - - {filtersMobileHeaderMarkup} - - {filtersContentMarkup} - {filtersMobileFooterMarkup} - - - ) : ( - -
- {filtersDesktopHeaderMarkup} - - {filtersContentMarkup} - - {filtersDesktopFooterMarkup} -
-
- ); - - const helpTextMarkup = helpText ? ( -
- - {helpText} - -
- ) : null; - - return ( - -
- {filtersControlMarkup} - {filtersContainerMarkup} - {tagsMarkup} - {helpTextMarkup} - {backdropMarkup} - +
+
+ {pinnedFiltersMarkup} + {addButton} + {clearAllMarkup} +
- - ); - } - - private hasAppliedFilters(): boolean { - const {appliedFilters, queryValue} = this.props; - const filtersApplied = Boolean(appliedFilters && appliedFilters.length > 0); - const queryApplied = Boolean(queryValue && queryValue !== ''); - - return filtersApplied || queryApplied; - } - - private getAppliedFilterContent(key: string): string | undefined { - const {appliedFilters} = this.props; - - if (!appliedFilters) { - return undefined; - } - - const filter = appliedFilters.find((filter) => filter.key === key); - - return filter == null ? undefined : filter.label; - } - - private getAppliedFilterRemoveHandler(key: string) { - const {appliedFilters} = this.props; - - if (!appliedFilters) { - return undefined; - } - - const filter = appliedFilters.find((filter) => filter.key === key); - - return filter == null ? undefined : filter.onRemove; - } - - private openFilters() { - this.setState({open: true}); - } - - private closeFilters = () => { - this.setState({open: false}, () => { - if (this.moreFiltersButtonContainer.current) { - focusFirstFocusableNode(this.moreFiltersButtonContainer.current, false); - } - }); - }; - - private toggleFilters = () => { - if (this.state.open === true) { - this.closeFilters(); - } else { - this.openFilters(); - } - }; - - private setReadyForFocus = (newState: boolean) => () => { - this.setState({readyForFocus: newState}); - }; - - private toggleFilter(key: string) { - if (this.state[`${key}${Suffix.Filter}`] === true) { - this.setState({readyForFocus: false, [`${key}${Suffix.Filter}`]: false}); - } else { - this.setState({readyForFocus: false, [`${key}${Suffix.Filter}`]: true}); - } - } - - private openFilterShortcut(key: string) { - this.setState({[`${key}${Suffix.Shortcut}`]: true}); - } - - private closeFilterShortcut(key: string) { - this.setState({[`${key}${Suffix.Shortcut}`]: false}); - } - - private toggleFilterShortcut(key: string) { - if (this.state[`${key}${Suffix.Shortcut}`] === true) { - this.closeFilterShortcut(key); - } else { - this.openFilterShortcut(key); - } - } - - private transformFilters(filters: FilterInterface[]) { - const transformedActions: Required< - ConnectedFilterControlProps['rightPopoverableActions'] - > = []; - - getShortcutFilters(filters).forEach((filter) => { - const {key, label, disabled} = filter; - - transformedActions.push({ - popoverContent: this.generateFilterMarkup(filter), - popoverOpen: Boolean(this.state[`${key}${Suffix.Shortcut}`]), - key, - content: label, - disabled, - onAction: () => this.toggleFilterShortcut(key), - }); - }); - return transformedActions; - } - - private generateFilterMarkup(filter: FilterInterface) { - const i18n = this.props.i18n; - const removeCallback = this.getAppliedFilterRemoveHandler(filter.key); - const removeHandler = - removeCallback == null - ? undefined - : () => { - removeCallback(filter.key); - }; - - const clearButtonMarkup = !filter.hideClearButton && ( - - ); - - return ( -
- - {filter.filter} - {clearButtonMarkup} - + {hideQueryField ? ( + + + {additionalContent} + + + ) : null}
); - } - - private handleClearAll = () => { - this.props.onClearAll(); - - this.moreFiltersDoneButtonContainer.current && - focusFirstFocusableNode( - this.moreFiltersDoneButtonContainer.current, - false, - ); - }; -} - -function getShortcutFilters(filters: FilterInterface[]) { - return filters.filter((filter) => filter.shortcut === true); -} - -export function Filters(props: FiltersProps) { - const i18n = useI18n(); - const mediaQuery = useMediaQuery(); - return ; + return ( +
+ {queryFieldMarkup} + {filtersMarkup} +
+ ); } diff --git a/polaris-react/src/components/Filters/components/ConnectedFilterControl/ConnectedFilterControl.scss b/polaris-react/src/components/Filters/components/ConnectedFilterControl/ConnectedFilterControl.scss deleted file mode 100644 index 0c4da91f141..00000000000 --- a/polaris-react/src/components/Filters/components/ConnectedFilterControl/ConnectedFilterControl.scss +++ /dev/null @@ -1,111 +0,0 @@ -@import '../../../../styles/common'; -// stylelint-disable selector-max-class -- generated by polaris-migrator DO NOT COPY -// stylelint-disable selector-max-combinators -- generated by polaris-migrator DO NOT COPY -// stylelint-disable selector-max-specificity -- generated by polaris-migrator DO NOT COPY -// stylelint-disable selector-max-compound-selectors -- generated by polaris-migrator DO NOT COPY -// stylelint-disable selector-max-type -- generated by polaris-migrator DO NOT COPY -.ConnectedFilterControl { - // stylelint-disable -- polaris/conventions/polaris/custom-property-allowed-list - --pc-connceted-filter-control-item: 10; - --pc-connceted-filter-control-focused: 20; - // stylelint-enable -- polaris/conventions/polaris/custom-property-allowed-list - display: flex; - flex-grow: 1; - - .CenterContainer { - flex: 1 1 auto; - min-width: 100px; - } - - &.right { - .CenterContainer * { - border-top-right-radius: var(--p-border-radius-1); - border-bottom-right-radius: var(--p-border-radius-1); - } - } -} - -.Item { - position: relative; - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - z-index: var(--pc-connceted-filter-control-item); -} - -.Item-focused { - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - z-index: var(--pc-connceted-filter-control-focused); -} - -.ProxyButtonContainer { - position: absolute; - top: -1000px; - left: -1000px; - display: flex; - width: 100%; - height: 0; - visibility: hidden; - overflow: hidden; - - > * { - flex-shrink: 0; - } -} - -.CenterContainer + .RightContainer, -.CenterContainer + .MoreFiltersButtonContainer { - margin-left: var(--p-space-2); -} - -.RightContainer { - display: flex; - - .Item > div > button { - margin-right: calc(-1 * var(--p-space-025)); - border-radius: 0; - } - - .Item { - flex-shrink: 0; - } - - .Item:first-of-type > div > button { - border-top-left-radius: var(--p-border-radius-1); - border-bottom-left-radius: var(--p-border-radius-1); - } -} - -.RightContainer.queryFieldHidden { - .Item:first-of-type > div > button { - border-top-left-radius: var(--p-border-radius-1); - border-bottom-left-radius: var(--p-border-radius-1); - } -} - -.RightContainerWithoutMoreFilters { - .Item:last-child > div > button { - border-top-right-radius: var(--p-border-radius-1); - border-bottom-right-radius: var(--p-border-radius-1); - } -} - -.MoreFiltersButtonContainer { - .Item > div > button { - white-space: nowrap; - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } -} - -.MoreFiltersButtonContainer.onlyButtonVisible { - .Item > div > button { - border-radius: var(--p-border-radius-1); - } -} - -.Wrapper { - display: flex; -} - -.AuxiliaryContainer { - flex-grow: 0; -} diff --git a/polaris-react/src/components/Filters/components/ConnectedFilterControl/ConnectedFilterControl.tsx b/polaris-react/src/components/Filters/components/ConnectedFilterControl/ConnectedFilterControl.tsx deleted file mode 100644 index 0c2c186e606..00000000000 --- a/polaris-react/src/components/Filters/components/ConnectedFilterControl/ConnectedFilterControl.tsx +++ /dev/null @@ -1,258 +0,0 @@ -import React, {Component, createRef} from 'react'; - -import {debounce} from '../../../../utilities/debounce'; -import {classNames} from '../../../../utilities/css'; -import type {DisableableAction} from '../../../../types'; -import {Popover} from '../../../Popover'; -import {Button} from '../../../Button'; -// eslint-disable-next-line import/no-deprecated -import {EventListener} from '../../../EventListener'; - -import {Item} from './components'; -import styles from './ConnectedFilterControl.scss'; - -interface PopoverableAction extends DisableableAction { - popoverOpen: boolean; - popoverContent: React.ReactNode; - key: string; - content: string; - onAction(): void; -} - -export interface ConnectedFilterControlProps { - children: React.ReactNode; - rightPopoverableActions?: PopoverableAction[] | null; - rightAction?: React.ReactNode; - auxiliary?: React.ReactNode; - disabled?: boolean; - forceShowMorefiltersButton?: boolean; - queryFieldHidden?: boolean; -} - -interface ComputedProperty { - [key: string]: number; -} - -interface State { - availableWidth: number; - proxyButtonsWidth: ComputedProperty; -} - -const FILTER_FIELD_MIN_WIDTH = 150; - -export class ConnectedFilterControl extends Component< - ConnectedFilterControlProps, - State -> { - state: State = { - availableWidth: 0, - proxyButtonsWidth: {}, - }; - - private container = createRef(); - private proxyButtonContainer = createRef(); - private moreFiltersButtonContainer = createRef(); - - private handleResize = debounce( - () => { - this.measureProxyButtons(); - this.measureAvailableWidth(); - }, - 40, - {leading: true, trailing: true, maxWait: 40}, - ); - - componentDidMount() { - this.handleResize(); - } - - render() { - const { - children, - rightPopoverableActions, - rightAction, - auxiliary, - forceShowMorefiltersButton = true, - queryFieldHidden, - } = this.props; - - const actionsToRender = - rightPopoverableActions != null - ? this.getActionsToRender(rightPopoverableActions) - : []; - - const className = classNames( - styles.ConnectedFilterControl, - rightPopoverableActions && styles.right, - ); - - const shouldRenderMoreFiltersButton = - forceShowMorefiltersButton || - (rightPopoverableActions && - rightPopoverableActions.length !== actionsToRender.length); - - const RightContainerClassName = classNames( - styles.RightContainer, - !shouldRenderMoreFiltersButton && styles.RightContainerWithoutMoreFilters, - queryFieldHidden && styles.queryFieldHidden, - ); - - const rightMarkup = - actionsToRender.length > 0 ? ( -
- {this.popoverFrom(actionsToRender)} -
- ) : null; - - const moreFiltersButtonContainerClassname = classNames( - styles.MoreFiltersButtonContainer, - actionsToRender.length === 0 && styles.onlyButtonVisible, - ); - - const rightActionMarkup = rightAction ? ( -
- {shouldRenderMoreFiltersButton && {rightAction}} -
- ) : null; - - const proxyButtonMarkup = rightPopoverableActions ? ( -
- {rightPopoverableActions.map((action) => ( -
- {this.activatorButtonFrom(action, {proxy: true})} -
- ))} -
- ) : null; - - const auxMarkup = auxiliary ? ( -
{auxiliary}
- ) : null; - - return ( - <> - {proxyButtonMarkup} -
-
- {children ? ( -
- {children} -
- ) : null} - {rightMarkup} - {rightActionMarkup} - -
- {auxMarkup} -
- - ); - } - - private measureProxyButtons() { - if (this.proxyButtonContainer.current) { - const proxyButtonsWidth: ComputedProperty = {}; - // this number is magical, but tweaking it solved the problem of items overlapping - const tolerance = 78; - if (this.proxyButtonContainer.current) { - Array.from(this.proxyButtonContainer.current.children).forEach( - (element: Element) => { - const buttonWidth = - element.getBoundingClientRect().width + tolerance; - const buttonKey = - element instanceof HTMLElement && element.dataset.key; - if (buttonKey) { - proxyButtonsWidth[buttonKey] = buttonWidth; - } - }, - ); - } - - this.setState({proxyButtonsWidth}); - } - } - - private measureAvailableWidth() { - if (this.container.current && this.moreFiltersButtonContainer.current) { - const containerWidth = - this.container.current.getBoundingClientRect().width; - const moreFiltersButtonWidth = - this.moreFiltersButtonContainer.current.getBoundingClientRect().width; - const filtersActionWidth = 0; - - const filterFieldMinWidth = this.props.queryFieldHidden - ? 0 - : FILTER_FIELD_MIN_WIDTH; - - const availableWidth = - containerWidth - - filterFieldMinWidth - - moreFiltersButtonWidth - - filtersActionWidth; - - this.setState({availableWidth}); - } - } - - private getActionsToRender( - actions: PopoverableAction[], - ): PopoverableAction[] { - let remainingWidth = this.state.availableWidth; - const actionsToReturn: PopoverableAction[] = []; - for (let i = 0; remainingWidth > 0 && i < actions.length; i++) { - const action = actions[i]; - const actionWidth = this.state.proxyButtonsWidth[action.key]; - if (actionWidth <= remainingWidth) { - actionsToReturn.push(action); - remainingWidth -= actionWidth; - } else { - // When we can't fit an action, we break the loop. - // The ones that didn't fit will be accessible through the "More filters" button - break; - } - } - return actionsToReturn; - } - - private activatorButtonFrom( - action: PopoverableAction, - options?: {proxy?: boolean}, - ): React.ReactElement { - const id = options?.proxy ? undefined : `Activator-${action.key}`; - return ( - - ); - } - - private popoverFrom(actions: PopoverableAction[]): React.ReactElement[] { - return actions.map((action) => { - return ( - - - {action.popoverContent} - - - ); - }); - } -} diff --git a/polaris-react/src/components/Filters/components/ConnectedFilterControl/components/Item/Item.tsx b/polaris-react/src/components/Filters/components/ConnectedFilterControl/components/Item/Item.tsx deleted file mode 100644 index 11406094383..00000000000 --- a/polaris-react/src/components/Filters/components/ConnectedFilterControl/components/Item/Item.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; - -import {classNames} from '../../../../../../utilities/css'; -import {useToggle} from '../../../../../../utilities/use-toggle'; -import styles from '../../ConnectedFilterControl.scss'; - -interface ItemProps { - children?: React.ReactNode; -} - -export function Item({children}: ItemProps) { - const { - value: focused, - setTrue: forceTrueFocused, - setFalse: forceFalseFocused, - } = useToggle(false); - - const className = classNames(styles.Item, focused && styles['Item-focused']); - - return ( -
- {children} -
- ); -} diff --git a/polaris-react/src/components/Filters/components/ConnectedFilterControl/components/Item/index.ts b/polaris-react/src/components/Filters/components/ConnectedFilterControl/components/Item/index.ts deleted file mode 100644 index c924835a042..00000000000 --- a/polaris-react/src/components/Filters/components/ConnectedFilterControl/components/Item/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Item'; diff --git a/polaris-react/src/components/Filters/components/ConnectedFilterControl/components/Item/tests/Item.test.tsx b/polaris-react/src/components/Filters/components/ConnectedFilterControl/components/Item/tests/Item.test.tsx deleted file mode 100644 index 7e5c00c33b3..00000000000 --- a/polaris-react/src/components/Filters/components/ConnectedFilterControl/components/Item/tests/Item.test.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import {mountWithApp} from 'tests/utilities'; - -import {Item} from '../Item'; - -describe('', () => { - it('handles focus', () => { - const item = mountWithApp(); - - item.find('div')!.trigger('onFocus'); - - expect(item).toContainReactComponent('div', { - className: expect.stringContaining('Item-focused'), - }); - }); - - it('handles blur', () => { - const item = mountWithApp(); - - item.find('div')!.trigger('onBlur'); - - expect(item).not.toContainReactComponent('div', { - className: expect.stringContaining('Item-focused'), - }); - }); -}); diff --git a/polaris-react/src/components/Filters/components/ConnectedFilterControl/components/index.ts b/polaris-react/src/components/Filters/components/ConnectedFilterControl/components/index.ts deleted file mode 100644 index c924835a042..00000000000 --- a/polaris-react/src/components/Filters/components/ConnectedFilterControl/components/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Item'; diff --git a/polaris-react/src/components/Filters/components/ConnectedFilterControl/index.ts b/polaris-react/src/components/Filters/components/ConnectedFilterControl/index.ts deleted file mode 100644 index fbbacc73aab..00000000000 --- a/polaris-react/src/components/Filters/components/ConnectedFilterControl/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ConnectedFilterControl'; diff --git a/polaris-react/src/components/Filters/components/ConnectedFilterControl/tests/ConnectedFilterControl.test.tsx b/polaris-react/src/components/Filters/components/ConnectedFilterControl/tests/ConnectedFilterControl.test.tsx deleted file mode 100644 index 970438de362..00000000000 --- a/polaris-react/src/components/Filters/components/ConnectedFilterControl/tests/ConnectedFilterControl.test.tsx +++ /dev/null @@ -1,263 +0,0 @@ -import React from 'react'; -import {mountWithApp} from 'tests/utilities'; - -import {Button} from '../../../../Button'; -import {Popover} from '../../../../Popover'; -import styles from '../ConnectedFilterControl.scss'; -import {ConnectedFilterControl} from '../ConnectedFilterControl'; -import type {ConnectedFilterControlProps} from '../ConnectedFilterControl'; - -const MockChild = () =>
; -const MockFilter = () =>
; -const MockAux = () =>
; - -type ArrayElement = T extends (infer U)[] ? U : never; - -type PopoverableAction = ArrayElement< - ConnectedFilterControlProps['rightPopoverableActions'] ->; - -const mockRightOpenPopoverableAction: PopoverableAction = { - popoverOpen: true, - popoverContent: , - key: 'openAction', - content: 'Open action', - onAction: noop, -}; - -const mockRightClosedPopoverableAction: PopoverableAction = { - popoverOpen: false, - popoverContent: , - key: 'closedAction', - content: 'Closed action', - onAction: noop, -}; - -const mockRightAction = ; - -describe('', () => { - it('mounts', () => { - expect(() => { - mountWithApp( - - - , - ); - }).not.toThrow(); - }); - - it('does not render buttons without right actions or right popoverable actions', () => { - const connectedFilterControl = mountWithApp( - - - , - ); - - expect(connectedFilterControl).not.toContainReactComponent(Button); - }); - - it('does not render popovers without right popoverable actions', () => { - const connectedFilterControl = mountWithApp( - - - , - ); - - expect(connectedFilterControl).not.toContainReactComponent(Popover); - }); - - it('always render a RightAction if forceShowMorefiltersButton is true', () => { - const connectedFilterControl = mountWithApp( - - - , - ); - - expect(connectedFilterControl).toContainReactComponent(Button); - }); - - it('renders a RightAction if forceShowMorefiltersButton is false and rightPopoverableActions do not fit on the "right action" container', () => { - const connectedFilterControl = mountWithApp( - - - , - ); - - expect(connectedFilterControl).toContainReactComponent(Button); - }); - - it('does not render a RightAction there are no actions hidden', () => { - const connectedFilterControl = mountWithApp( - - - , - ); - - expect(connectedFilterControl).not.toContainReactComponent(Button); - }); - - it('renders rightActionMarkup if rightAction is not null', () => { - const connectedFilterControl = mountWithApp( - - - , - ); - - expect(connectedFilterControl).toContainReactComponent('div', { - className: 'MoreFiltersButtonContainer onlyButtonVisible', - }); - }); - - it('does not render rightActionMarkup if rightAction is null', () => { - const connectedFilterControl = mountWithApp( - - - , - ); - - expect(connectedFilterControl).not.toContainReactComponent('div', { - className: 'MoreFiltersButtonContainer onlyButtonVisible', - }); - }); - - it('does render a button with a popoverable action', () => { - const connectedFilterControl = mountWithApp( - - - , - ); - - expect(connectedFilterControl).toContainReactComponentTimes(Button, 1); - }); - - it('renders three buttons with two popoverable actions and a right action', () => { - const connectedFilterControl = mountWithApp( - - - , - ); - - expect(connectedFilterControl).toContainReactComponentTimes(Button, 3); - }); - - it('hides an action if it does not fit', () => { - const connectedFilterControl = mountWithApp( - - - , - ); - - connectedFilterControl.instance.setState({availableWidth: 100}); - - connectedFilterControl.forceUpdate(); - - const wrapper = connectedFilterControl.find('div', { - className: styles.Wrapper, - })!; - - expect(wrapper).toContainReactComponentTimes(Button, 2); - }); - - it('renders auxiliary content', () => { - const connectedFilterControl = mountWithApp( - }> - - , - ); - - expect(connectedFilterControl).toContainReactComponent(MockAux); - }); - - it('only disables activators of inactive rightPopoverableActions', () => { - const connectedFilterControl = mountWithApp( - - - , - ); - - expect(connectedFilterControl).toContainReactComponentTimes(Button, 1, { - disclosure: true, - disabled: true, - }); - }); - - it('disables each activator buttons when prop disabled is passed', () => { - const rightPopoverableActions = [ - mockRightClosedPopoverableAction, - mockRightOpenPopoverableAction, - ]; - - const connectedFilterControl = mountWithApp( - - - , - ); - - expect(connectedFilterControl).toContainReactComponentTimes(Button, 2, { - disclosure: true, - disabled: true, - }); - }); - - it('does not render CenterContainer when no child element is "null"', () => { - const connectedFilterControl = mountWithApp( - {null}, - ); - - expect(connectedFilterControl).not.toContainReactComponent('div', { - className: expect.stringContaining('CenterContainer'), - }); - }); - - it('renders CenterContainer when no child element is not "null"', () => { - const connectedFilterControl = mountWithApp( - - - , - ); - - expect(connectedFilterControl).toContainReactComponentTimes('div', 1, { - className: expect.stringContaining('CenterContainer'), - }); - }); -}); - -function noop() {} diff --git a/polaris-react/src/components/AlphaFilters/components/FilterPill/FilterPill.scss b/polaris-react/src/components/Filters/components/FilterPill/FilterPill.scss similarity index 84% rename from polaris-react/src/components/AlphaFilters/components/FilterPill/FilterPill.scss rename to polaris-react/src/components/Filters/components/FilterPill/FilterPill.scss index ed81c19b99d..1d4ff248e89 100644 --- a/polaris-react/src/components/AlphaFilters/components/FilterPill/FilterPill.scss +++ b/polaris-react/src/components/Filters/components/FilterPill/FilterPill.scss @@ -7,9 +7,9 @@ cursor: pointer; color: var(--p-color-text); - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY + // stylelint-disable-next-line -- no way to set focus ring without mixin currently @include focus-ring($border-width: var(--p-border-width-1)); - // stylelint-disable-next-line selector-max-specificity -- generated by polaris-migrator DO NOT COPY + // stylelint-disable-next-line selector-max-specificity -- specificity is required to override focus-ring mixin &.focusedFilterButton:focus-within:not(:active) { // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY @include focus-ring($style: focused); @@ -86,7 +86,7 @@ } .clearButton { - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY + // stylelint-disable-next-line -- no way to set focus ring without mixin currently @include focus-ring; margin-right: var(--p-space-2); @@ -95,7 +95,7 @@ } &:focus-visible:not(:active) { - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY + // stylelint-disable-next-line -- no way to set focus ring without mixin currently @include focus-ring($style: focused); } } diff --git a/polaris-react/src/components/AlphaFilters/components/FilterPill/FilterPill.tsx b/polaris-react/src/components/Filters/components/FilterPill/FilterPill.tsx similarity index 100% rename from polaris-react/src/components/AlphaFilters/components/FilterPill/FilterPill.tsx rename to polaris-react/src/components/Filters/components/FilterPill/FilterPill.tsx diff --git a/polaris-react/src/components/AlphaFilters/components/FilterPill/index.ts b/polaris-react/src/components/Filters/components/FilterPill/index.ts similarity index 100% rename from polaris-react/src/components/AlphaFilters/components/FilterPill/index.ts rename to polaris-react/src/components/Filters/components/FilterPill/index.ts diff --git a/polaris-react/src/components/AlphaFilters/components/FilterPill/tests/FilterPill.test.tsx b/polaris-react/src/components/Filters/components/FilterPill/tests/FilterPill.test.tsx similarity index 100% rename from polaris-react/src/components/AlphaFilters/components/FilterPill/tests/FilterPill.test.tsx rename to polaris-react/src/components/Filters/components/FilterPill/tests/FilterPill.test.tsx diff --git a/polaris-react/src/components/AlphaFilters/components/SearchField/SearchField.tsx b/polaris-react/src/components/Filters/components/SearchField/SearchField.tsx similarity index 100% rename from polaris-react/src/components/AlphaFilters/components/SearchField/SearchField.tsx rename to polaris-react/src/components/Filters/components/SearchField/SearchField.tsx diff --git a/polaris-react/src/components/AlphaFilters/components/SearchField/index.ts b/polaris-react/src/components/Filters/components/SearchField/index.ts similarity index 100% rename from polaris-react/src/components/AlphaFilters/components/SearchField/index.ts rename to polaris-react/src/components/Filters/components/SearchField/index.ts diff --git a/polaris-react/src/components/AlphaFilters/components/SearchField/tests/SearchField.test.tsx b/polaris-react/src/components/Filters/components/SearchField/tests/SearchField.test.tsx similarity index 100% rename from polaris-react/src/components/AlphaFilters/components/SearchField/tests/SearchField.test.tsx rename to polaris-react/src/components/Filters/components/SearchField/tests/SearchField.test.tsx diff --git a/polaris-react/src/components/Filters/components/TagsWrapper/TagsWrapper.tsx b/polaris-react/src/components/Filters/components/TagsWrapper/TagsWrapper.tsx deleted file mode 100644 index 41e32308780..00000000000 --- a/polaris-react/src/components/Filters/components/TagsWrapper/TagsWrapper.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; - -import {Text} from '../../../Text'; - -interface Props { - children: React.ReactNode; - hidden: boolean; -} - -export function TagsWrapper({children, hidden}: Props) { - if (hidden) { - return ( - - {children} - - ); - } - - return <>{children}; -} diff --git a/polaris-react/src/components/Filters/components/TagsWrapper/index.ts b/polaris-react/src/components/Filters/components/TagsWrapper/index.ts deleted file mode 100644 index 25963651dab..00000000000 --- a/polaris-react/src/components/Filters/components/TagsWrapper/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './TagsWrapper'; diff --git a/polaris-react/src/components/Filters/components/TagsWrapper/tests/TagsWrapper.test.tsx b/polaris-react/src/components/Filters/components/TagsWrapper/tests/TagsWrapper.test.tsx deleted file mode 100644 index 9bb1d8c0994..00000000000 --- a/polaris-react/src/components/Filters/components/TagsWrapper/tests/TagsWrapper.test.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; -import {mountWithApp} from 'tests/utilities'; - -import {TagsWrapper} from '../TagsWrapper'; -import {Text} from '../../../../Text'; - -const MockChild = () =>
; - -describe('', () => { - it('renders visually hidden component when hidden is true', () => { - const tagsWrapper = mountWithApp( - , - ); - - expect(tagsWrapper).toContainReactComponent(Text, {visuallyHidden: true}); - expect( - tagsWrapper.find(Text, {visuallyHidden: true}), - ).toContainReactComponent(MockChild); - }); - - it('renders children directly when hidden is false', () => { - const tagsWrapper = mountWithApp( - , - ); - - expect(tagsWrapper).not.toContainReactComponent(Text, { - visuallyHidden: true, - }); - expect(tagsWrapper).toContainReactComponent(MockChild); - }); -}); diff --git a/polaris-react/src/components/Filters/components/index.ts b/polaris-react/src/components/Filters/components/index.ts index 435cbb92402..2bb64cad0c9 100644 --- a/polaris-react/src/components/Filters/components/index.ts +++ b/polaris-react/src/components/Filters/components/index.ts @@ -1,2 +1,2 @@ -export * from './ConnectedFilterControl'; -export * from './TagsWrapper'; +export * from './FilterPill'; +export * from './SearchField'; diff --git a/polaris-react/src/components/Filters/index.ts b/polaris-react/src/components/Filters/index.ts index c7b59bcd2f6..87f7f476649 100644 --- a/polaris-react/src/components/Filters/index.ts +++ b/polaris-react/src/components/Filters/index.ts @@ -1 +1,2 @@ export * from './Filters'; +export * from './components'; diff --git a/polaris-react/src/components/Filters/tests/Filters.test.tsx b/polaris-react/src/components/Filters/tests/Filters.test.tsx index fe33b70cbdc..615983fb312 100644 --- a/polaris-react/src/components/Filters/tests/Filters.test.tsx +++ b/polaris-react/src/components/Filters/tests/Filters.test.tsx @@ -2,778 +2,323 @@ import React from 'react'; import {matchMedia} from '@shopify/jest-dom-mocks'; import {mountWithApp} from 'tests/utilities'; -import {Button} from '../../Button'; -import type {ButtonProps} from '../../Button'; -import {Collapsible} from '../../Collapsible'; -import {Popover} from '../../Popover'; -// eslint-disable-next-line import/no-deprecated -import {Sheet} from '../../Sheet'; -import {Tag} from '../../Tag'; -import {TextField} from '../../TextField'; -import {Text} from '../../Text'; -import {WithinFilterContext} from '../../../utilities/within-filter-context'; +import {ActionList} from '../../ActionList'; import {Filters} from '../Filters'; import type {FiltersProps} from '../Filters'; -import {ConnectedFilterControl, TagsWrapper} from '../components'; -import * as focusUtils from '../../../utilities/focus'; -import styles from '../Filters.scss'; -import {Focus} from '../../Focus'; - -const MockFilter = (props: {id: string}) =>
; -const MockChild = () =>
; -const mockProps: FiltersProps = { - onQueryChange: noop, - onQueryClear: noop, - onClearAll: noop, - filters: [ - { - key: 'filterOne', - label: 'Filter One', - filter: , - }, - { - key: 'filterTwo', - label: 'Filter Two', - filter: , - disabled: true, - }, - { - key: 'filterThree', - label: 'Filter Three', - filter: , - }, - ], -}; +import {FilterPill} from '../components'; describe('', () => { - beforeAll(() => { - jest.useFakeTimers(); - }); + let originalScroll: any; beforeEach(() => { + originalScroll = HTMLElement.prototype.scroll; matchMedia.mock(); }); afterEach(() => { + HTMLElement.prototype.scroll = originalScroll; matchMedia.restore(); }); - it('renders WithinFilterContext with a value of true', () => { - WithinFilterContext; - const filters = mountWithApp(); - - expect(filters).toContainReactComponentTimes( - WithinFilterContext.Provider, - 1, + const defaultProps: FiltersProps = { + filters: [ { - value: true, + key: 'foo', + label: 'Foo', + pinned: false, + filter:
Filter
, }, - ); - }); - - it('calls the onQueryFocus callback when the query field is focused', () => { - const onQueryFocus = jest.fn(); - const filters = mountWithApp( - , - ); - - filters.find(TextField)!.trigger('onFocus'); - - expect(onQueryFocus).toHaveBeenCalledTimes(1); - }); - - it('does not render the TextField when "hideQueryField" is "true"', () => { - const filters = mountWithApp(); - - expect(filters).not.toContainReactComponent(TextField); - }); - - it('renders the TextField when "hideQueryField" is false', () => { - const filters = mountWithApp(); - - expect(filters).toContainReactComponent(TextField); - }); - - describe('toggleFilters()', () => { - it('opens the sheet on toggle button click', () => { - const resourceFilters = mountWithApp(); - - resourceFilters - .find(Button, {children: 'More filters'})! - .trigger('onClick'); - jest.runAllTimers(); - - // eslint-disable-next-line import/no-deprecated - expect(resourceFilters).toContainReactComponent(Sheet, {open: true}); - }); - - it('closes the sheet on second toggle button click', () => { - const resourceFilters = mountWithApp(); - - resourceFilters - .find(Button, {children: 'More filters'})! - .trigger('onClick'); - resourceFilters - .find(Button, {children: 'More filters'})! - .trigger('onClick'); - - // eslint-disable-next-line import/no-deprecated - expect(resourceFilters).toContainReactComponent(Sheet, {open: false}); - }); - - describe('isMobile()', () => { - it('renders a sheet on desktop size with right origin', () => { - const resourceFilters = mountWithApp(); - - // eslint-disable-next-line import/no-deprecated - expect(resourceFilters).toContainReactComponent(Sheet); - }); - - it('renders a sheet on mobile size with bottom origin', () => { - matchMedia.setMedia(() => ({matches: true})); - const resourceFilters = mountWithApp(); - - // eslint-disable-next-line import/no-deprecated - expect(resourceFilters).toContainReactComponent(Sheet); - }); - - it('opens the sheet at mobile size on toggle button click', () => { - matchMedia.setMedia(() => ({matches: true})); - const resourceFilters = mountWithApp(); - - resourceFilters - .find(Button, {children: 'More filters'})! - .trigger('onClick'); - - // eslint-disable-next-line import/no-deprecated - expect(resourceFilters).toContainReactComponent(Sheet, {open: true}); - }); - - it('closes the sheet at mobile size on second toggle button click', () => { - matchMedia.setMedia(() => ({matches: true})); - const resourceFilters = mountWithApp(); - - resourceFilters - .find(Button, {children: 'More filters'})! - .trigger('onClick'); - resourceFilters - .find(Button, {children: 'More filters'})! - .trigger('onClick'); - - // eslint-disable-next-line import/no-deprecated - expect(resourceFilters).toContainReactComponent(Sheet, {open: false}); - }); + { + key: 'bar', + label: 'Bar', + pinned: true, + filter:
Filter
, + }, + { + key: 'baz', + label: 'Baz', + pinned: false, + filter:
Filter
, + }, + ], + appliedFilters: [], + onQueryChange: jest.fn(), + onQueryClear: jest.fn(), + onClearAll: jest.fn(), + }; + + it('renders a list of pinned filters', () => { + const scrollSpy = jest.fn(); + HTMLElement.prototype.scroll = scrollSpy; + const wrapper = mountWithApp(); + + expect(wrapper).toContainReactComponentTimes(FilterPill, 1); + expect(wrapper).toContainReactComponent(FilterPill, { + label: defaultProps.filters[1].label, }); }); - describe('toggleFilter()', () => { - it('opens the filter on toggle button click', () => { - const resourceFilters = mountWithApp(); - - resourceFilters - .find(Button, {children: 'More filters'})! - .trigger('onClick'); - - resourceFilters - .find('button', {id: 'filterOneToggleButton'})! - .trigger('onClick'); - - expect(resourceFilters).toContainReactComponent(Collapsible, { - id: 'filterOneCollapsible', - open: true, - }); - }); - - it('closes the filter on second toggle button click', () => { - const resourceFilters = mountWithApp(); - - resourceFilters - .find(Button, {children: 'More filters'})! - .trigger('onClick'); - - const button = resourceFilters.find('button', { - id: 'filterTwoToggleButton', - }); - - button!.trigger('onClick'); - button!.trigger('onClick'); - - expect(resourceFilters).toContainReactComponent(Collapsible, { - id: 'filterTwoCollapsible', - open: false, - }); - }); - - it('does not close other filters when a filter is toggled', () => { - const resourceFilters = mountWithApp(); - - resourceFilters - .find(Button, {children: 'More filters'})! - .trigger('onClick'); - - resourceFilters - .find('button', {id: 'filterOneToggleButton'})! - .trigger('onClick'); + it('renders the unpinned filters inside a Popover', () => { + const scrollSpy = jest.fn(); + HTMLElement.prototype.scroll = scrollSpy; + const wrapper = mountWithApp(); - resourceFilters - .find('button', {id: 'filterThreeToggleButton'})! + wrapper.act(() => { + wrapper + .find('button', { + 'aria-label': 'Add filter', + })! .trigger('onClick'); - - expect(resourceFilters).toContainReactComponent(Collapsible, { - id: 'filterOneCollapsible', - open: true, - }); - - expect(resourceFilters).toContainReactComponent(Collapsible, { - id: 'filterThreeCollapsible', - open: true, - }); }); - }); - describe('', () => { - const mockPropsWithShortcuts: FiltersProps = { - onQueryChange: noop, - onQueryClear: noop, - onClearAll: noop, - filters: [ - { - key: 'filterOne', - label: 'Filter One', - filter: , - shortcut: true, - }, - { - key: 'filterTwo', - label: 'Filter Two', - filter: , - }, - { - key: 'filterThree', - label: 'Filter Three', - filter: , - shortcut: true, - }, + expect(wrapper).toContainReactComponent(ActionList, { + items: [ + expect.objectContaining({content: defaultProps.filters[0].label}), + expect.objectContaining({content: defaultProps.filters[2].label}), ], - }; - - it('renders', () => { - const resourceFilters = mountWithApp( - , - ); - - expect(resourceFilters).toContainReactComponent(ConnectedFilterControl); - }); - - it('renders children', () => { - const resourceFilters = mountWithApp( - - - , - ); - - expect(resourceFilters).toContainReactComponent(MockChild); - }); - - it('receives the expected props when there are shortcut filters', () => { - const resourceFilters = mountWithApp( - , - ); - - expect( - resourceFilters.find(ConnectedFilterControl)!.props - .rightPopoverableActions, - ).toHaveLength(2); - }); - - it('receives the expected props when the query field is hidden', () => { - const resourceFilters = mountWithApp( - , - ); - - expect(resourceFilters).toContainReactComponent(ConnectedFilterControl, { - queryFieldHidden: true, - }); - }); - - it('forces showing the "More Filters" button if there are filters without shortcuts', () => { - const resourceFilters = mountWithApp( - , - ); - - expect(resourceFilters).toContainReactComponent(ConnectedFilterControl, { - forceShowMorefiltersButton: true, - }); - }); - - it('does not force showing the "More Filters" button if all the filters have shorcuts', () => { - const mockPropsWithShortcuts: FiltersProps = { - onQueryChange: noop, - onQueryClear: noop, - onClearAll: noop, - filters: [ - { - key: 'filterOne', - label: 'Filter One', - filter: , - shortcut: true, - }, - { - key: 'filterTwo', - label: 'Filter Two', - filter: , - shortcut: true, - }, - ], - }; - const resourceFilters = mountWithApp( - , - ); - expect(resourceFilters).toContainReactComponent(ConnectedFilterControl, { - forceShowMorefiltersButton: false, - }); - }); - - it('receives shortcut filters with popoverOpen set to false on mount', () => { - const resourceFilters = mountWithApp( - , - ); - - const rightPopoverableActions = resourceFilters.find( - ConnectedFilterControl, - )!.props.rightPopoverableActions; - - rightPopoverableActions!.forEach((action) => { - expect(action.popoverOpen).toBe(false); - }); - }); - - it('toggles a shortcut filter', () => { - const resourceFilters = mountWithApp( - , - ); - - const connected = resourceFilters.find(ConnectedFilterControl); - connected!.instance.setState({availableWidth: 999}); - resourceFilters.forceUpdate(); - const shortcut = resourceFilters - .find('div', {className: styles.RightContainer})! - .find(Button); - - shortcut!.trigger('onClick'); - expect(resourceFilters).toContainReactComponent(Popover, {active: true}); - shortcut!.trigger('onClick'); - expect(resourceFilters).toContainReactComponent(Popover, {active: false}); - }); - - it('receives the expected props when there are no shortcut filters', () => { - const resourceFilters = mountWithApp(); - - expect(resourceFilters).toContainReactComponent(ConnectedFilterControl, { - rightPopoverableActions: [], - }); - }); - - it('does not render the filter button when no filters are passed in', () => { - const resourceFilters = mountWithApp( - , - ); - expect(resourceFilters).not.toContainReactComponent(Button, { - children: 'More filters', - } as ButtonProps); }); }); - describe('appliedFilters', () => { - it('calls remove callback when tag is clicked', () => { - const spy = jest.fn(); - const appliedFilters = [{key: 'filterOne', label: 'foo', onRemove: spy}]; - - const resourceFilters = mountWithApp( - , - ); - - const tag = resourceFilters.find(Tag); - const removeButton = tag!.find('button'); - - removeButton!.trigger('onClick'); - expect(spy).toHaveBeenCalledTimes(1); - expect(spy).toHaveBeenCalledWith('filterOne'); - }); - - it('calls remove callback when clear button is clicked', () => { - const spy = jest.fn(); - const appliedFilters = [{key: 'filterOne', label: 'foo', onRemove: spy}]; - - const resourceFilters = mountWithApp( - , - ); - - resourceFilters - .find(Button, {children: 'More filters'})! - .trigger('onClick'); - - resourceFilters - .find('button', {id: 'filterOneToggleButton'})! - .trigger('onClick'); - const collapsible = resourceFilters.find(Collapsible, { - id: 'filterOneCollapsible', - }); - const buttons = collapsible!.findAll(Button); - // last button - const clearButton = buttons[buttons.length - 1]; - - clearButton!.trigger('onClick'); - - expect(spy).toHaveBeenCalledTimes(1); - expect(spy).toHaveBeenCalledWith('filterOne'); - }); - - it('renders a clear button when clearButton is not provided', () => { - const filters = [ - {key: 'filterOne', label: 'foo', onRemove: () => {}, filter: null}, - ]; + it('renders the unpinned disabled filters inside a Popover', () => { + const scrollSpy = jest.fn(); + HTMLElement.prototype.scroll = scrollSpy; + const filters = [ + ...defaultProps.filters, + { + key: 'disabled', + label: 'Disabled', + pinned: false, + disabled: true, + filter:
Filter
, + }, + ]; - const resourceFilters = mountWithApp( - , - ); + const wrapper = mountWithApp( + , + ); - resourceFilters - .find(Button, {children: 'More filters'})! - .trigger('onClick'); - resourceFilters - .find('button', {id: 'filterOneToggleButton'})! + wrapper.act(() => { + wrapper + .find('button', { + 'aria-label': 'Add filter', + })! .trigger('onClick'); - - const collapsible = resourceFilters.find(Collapsible, { - id: 'filterOneCollapsible', - }); - - expect(collapsible).toContainReactText('Clear'); }); - it("doesn't render a clear button when clearButton is not provided", () => { - const filters = [ - { - hideClearButton: true, - key: 'filterOne', - label: 'foo', - onRemove: () => {}, - filter: null, - }, - ]; - - const resourceFilters = mountWithApp( - , - ); - - resourceFilters - .find(Button, {children: 'More filters'})! - .trigger('onClick'); - resourceFilters - .find('button', {id: 'filterOneToggleButton'})! - .trigger('onClick'); - - const collapsible = resourceFilters.find(Collapsible, { - id: 'filterOneCollapsible', - }); - - expect(collapsible).not.toContainReactText('Clear'); + expect(wrapper).toContainReactComponent(ActionList, { + items: [ + expect.objectContaining({ + content: filters[0].label, + }), + expect.objectContaining({ + content: filters[2].label, + }), + expect.objectContaining({ + content: filters[3].label, + disabled: true, + }), + ], }); + }); - it('tags are not shown if hideTags prop is given', () => { - const appliedFilters = [{key: 'filterOne', label: 'foo', onRemove: noop}]; - - const resourceFilters = mountWithApp( - , - ); - expect(resourceFilters).not.toContainReactComponent(Tag); - }); + it('renders an applied filter', () => { + const scrollSpy = jest.fn(); + HTMLElement.prototype.scroll = scrollSpy; + const appliedFilters = [ + { + ...defaultProps.filters[2], + label: 'Bux', + value: ['Bux'], + onRemove: jest.fn(), + }, + ]; + const wrapper = mountWithApp( + , + ); - it('hides the tags container when applied filters are not provided', () => { - const resourceFilters = mountWithApp(); - expect(resourceFilters).toContainReactComponent(TagsWrapper, { - hidden: true, - }); + expect(wrapper).toContainReactComponentTimes(FilterPill, 2); + expect(wrapper.findAll(FilterPill)[1]).toHaveReactProps({ + label: 'Bux', + selected: true, }); + }); - it('renders applied filters container with aria live', () => { - const resourceFilters = mountWithApp(); - expect(resourceFilters).toContainReactComponent('div', { - className: 'TagsContainer', - 'aria-live': 'polite', - }); - }); + it('will not open the popover for an applied filter by default', () => { + const appliedFilters = [ + { + ...defaultProps.filters[2], + label: 'Bux', + value: ['Bux'], + onRemove: jest.fn(), + }, + ]; + const wrapper = mountWithApp( + , + ); - it('applied filter count is shown if hideTags prop is given', () => { - const appliedFilters = [ - {key: 'filterOne', label: 'foo', onRemove: noop}, - {key: 'filterTwo', label: 'bar', onRemove: noop}, - ]; - - const resourceFilters = mountWithApp( - , - ); - - expect(resourceFilters).toContainReactComponent(Button, { - children: 'More filters (2)', - }); + expect(wrapper).toContainReactComponentTimes(FilterPill, 2); + expect(wrapper.findAll(FilterPill)[1]).toHaveReactProps({ + label: 'Bux', + initialActive: false, }); + }); - it('calls clear all callback and shifts focus when clear all filters is clicked to prevent visual loss of focus', () => { - const focusSpy = jest.spyOn(focusUtils, 'focusFirstFocusableNode'); - const spy = jest.fn(); - const appliedFilters = [{key: 'filterOne', label: 'foo', onRemove: spy}]; - - const resourceFilters = mountWithApp( - , - ); - resourceFilters.setProps({ - onClearAll: () => { - resourceFilters.setProps({ - appliedFilters: [], - }); - }, - }); - - resourceFilters - .find(Button, {children: 'More filters'})! - .trigger('onClick'); - - resourceFilters - .find(Button, { - children: 'Clear all filters', - disabled: false, + it('triggers the onAddFilterClick callback when the add filter button is clicked', () => { + const callbackFunction = jest.fn(); + const wrapper = mountWithApp( + , + ); + wrapper.act(() => { + wrapper + .find('button', { + 'aria-label': 'Add filter', })! .trigger('onClick'); - - expect(resourceFilters).toContainReactComponent(Button, { - children: 'Clear all filters', - disabled: true, - }); - expect(focusSpy).toHaveBeenCalled(); - focusSpy.mockRestore(); }); + + expect(callbackFunction).toHaveBeenCalled(); + expect(wrapper).toContainReactComponent(ActionList); }); - describe('disableQueryField', () => { - it('does not disable the TextField by default', () => { - const resourceFilters = mountWithApp(); + it('correctly invokes the onRemove callback when clicking on an applied filter', () => { + const scrollSpy = jest.fn(); + HTMLElement.prototype.scroll = scrollSpy; + const appliedFilters = [ + { + ...defaultProps.filters[2], + label: 'Bux', + value: ['Bux'], + onRemove: jest.fn(), + }, + ]; + const wrapper = mountWithApp( + , + ); - expect(resourceFilters).toContainReactComponent(TextField, { - disabled: false, - }); + wrapper.act(() => { + wrapper.findAll(FilterPill)[1].findAll('button')[1].trigger('onClick'); }); - it('does not disable the search field when false', () => { - const resourceFilters = mountWithApp( - , - ); + expect(appliedFilters[0].onRemove).toHaveBeenCalled(); + }); - expect(resourceFilters).toContainReactComponent(TextField, { - disabled: false, - }); - }); + it('will not render the add badge if all filters are pinned by default', () => { + const scrollSpy = jest.fn(); + HTMLElement.prototype.scroll = scrollSpy; + const filters = defaultProps.filters.map((filter) => ({ + ...filter, + pinned: true, + })); - it('disables the search field when true', () => { - const resourceFilters = mountWithApp( - , - ); + const wrapper = mountWithApp( + , + ); - expect(resourceFilters).toContainReactComponent(TextField, { - disabled: true, - }); + expect(wrapper).not.toContainReactComponent('div', { + className: 'AddFilterActivator', }); }); - describe('disabled', () => { - it('disables the search field when true', () => { - const resourceFilters = mountWithApp( - , - ); - - expect(resourceFilters).toContainReactComponent(TextField, { + it('will not render a disabled filter if pinned', () => { + const scrollSpy = jest.fn(); + HTMLElement.prototype.scroll = scrollSpy; + const filters = [ + ...defaultProps.filters, + { + key: 'disabled', + label: 'Disabled', + pinned: true, disabled: true, - }); - }); - - it('disables when true', () => { - const resourceFilters = mountWithApp(); - - const rightActionButton = resourceFilters.find(Button, { - children: 'More filters', - }); - - expect(rightActionButton).toHaveReactProps({disabled: true}); - }); - - it('passes disabled prop to connected filters', () => { - const resourceFilters = mountWithApp( - , - ); + filter:
Filter
, + }, + ]; - expect(resourceFilters).toContainReactComponent(ConnectedFilterControl, { - disabled: true, - }); - }); + const wrapper = mountWithApp( + , + ); - it('subdues each filter headings is mounted with prop disabled as true', () => { - const resourceFilters = mountWithApp(); + expect(wrapper).toContainReactComponentTimes(FilterPill, 2); - resourceFilters - .find(Button, {children: 'More filters'})! + wrapper.act(() => { + wrapper + .find('button', { + 'aria-label': 'Add filter', + })! .trigger('onClick'); - - mockProps.filters.forEach((filter) => { - const toggleButton = resourceFilters.find('button', { - id: `${filter.key}ToggleButton`, - }); - - expect(toggleButton!).toContainReactComponent(Text, { - color: 'subdued', - }); - }); }); - it('is passed to with set value', () => { - const appliedFilters = [{key: 'filterOne', label: 'foo', onRemove: noop}]; - - const resourceFilters = mountWithApp( - , - ); - - resourceFilters.findAll(Tag).forEach((tag) => { - expect(tag).toHaveReactProps({disabled: true}); - }); + expect(wrapper).toContainReactComponent(ActionList, { + items: [ + expect.objectContaining({content: defaultProps.filters[0].label}), + expect.objectContaining({content: defaultProps.filters[2].label}), + ], }); - describe('individual filters', () => { - it('subdues disabled filters heading', () => { - const resourceFilters = mountWithApp(); - - resourceFilters - .find(Button, {children: 'More filters'})! - .trigger('onClick'); - - mockProps.filters - .filter(({disabled}) => disabled) - .forEach((filter) => { - const toggleButton = resourceFilters.find('button', { - id: `${filter.key}ToggleButton`, - }); - - expect(toggleButton).toContainReactComponent(Text, { - color: 'subdued', - }); - }); - }); - - it('does not subdue active filters heading', () => { - const resourceFilters = mountWithApp(); - - resourceFilters - .find(Button, {children: 'More filters'})! - .trigger('onClick'); - - mockProps.filters - .filter(({disabled}) => !disabled) - .forEach((filter) => { - const toggleButton = resourceFilters.find('button', { - id: `${filter.key}ToggleButton`, - }); - - expect(toggleButton).toContainReactComponent(Text, { - color: undefined, - }); - }); - }); - }); + expect(wrapper.findAll(FilterPill)[1].domNode).toBeNull(); }); - describe('helpText', () => { - it('renders a subdued when provided', () => { - const helpText = 'Important filters information'; - const resourceFilters = mountWithApp( - , - ); - - const helpTextMarkup = resourceFilters.findAll('div', { - id: 'FiltersHelpText', - }); - expect(helpTextMarkup).toHaveLength(1); - expect(helpTextMarkup[0]).toContainReactComponent(Text, { - children: helpText, - }); - }); - - it('is not rendered when not provided', () => { - const resourceFilters = mountWithApp(); - - expect(resourceFilters).not.toContainReactComponent('div', { - id: 'FiltersHelpText', - }); - }); - }); - - describe('readyForFocus', () => { - it('unfocuses the filter when a filter is toggled', () => { - const resourceFilters = mountWithApp(); + it('renders filters with sections', () => { + const filtersWithSections = [ + { + key: 'sectionfilter1', + label: 'SF1', + pinned: false, + filter:
SF1
, + section: 'Section One', + }, + { + key: 'sectionfilter2', + label: 'SF2', + pinned: false, + filter:
SF1
, + section: 'Section Two', + }, + { + key: 'sectionfilter3', + label: 'SF3', + pinned: false, + filter:
SF3
, + section: 'Section One', + }, + ]; - resourceFilters - .find(Button, {children: 'More filters'})! - .trigger('onClick'); + const wrapper = mountWithApp( + , + ); - resourceFilters - .find('button', {id: 'filterOneToggleButton'})! + wrapper.act(() => { + wrapper + .find('button', { + 'aria-label': 'Add filter', + })! .trigger('onClick'); - - expect( - resourceFilters.find(Collapsible, {id: 'filterOneCollapsible'})!, - ).toContainReactComponent(Focus, { - disabled: true, - }); }); - it('focuses the filter when Collapsible is opened', () => { - const resourceFilters = mountWithApp(); - - resourceFilters - .find(Button, {children: 'More filters'})! - .trigger('onClick'); - - resourceFilters - .find('button', {id: 'filterOneToggleButton'})! - .trigger('onClick'); - - expect( - resourceFilters.find(Collapsible, {id: 'filterOneCollapsible'})!, - ).toContainReactComponent(Focus, { - disabled: true, - }); - - resourceFilters.find(Collapsible)!.trigger('onAnimationEnd'); - - expect( - resourceFilters.find(Collapsible, {id: 'filterOneCollapsible'})!, - ).toContainReactComponent(Focus, { - disabled: false, - }); + expect(wrapper).toContainReactComponent(ActionList, { + sections: [ + expect.objectContaining({ + title: 'Section One', + items: [ + expect.objectContaining({ + content: filtersWithSections[0].label, + }), + expect.objectContaining({ + content: filtersWithSections[2].label, + }), + ], + }), + expect.objectContaining({ + title: 'Section Two', + items: [ + expect.objectContaining({ + content: filtersWithSections[1].label, + }), + ], + }), + ], }); }); }); - -function noop() {} diff --git a/polaris-react/src/components/FooterHelp/FooterHelp.scss b/polaris-react/src/components/FooterHelp/FooterHelp.scss index ddf04d8bc80..c4f4c2d05eb 100644 --- a/polaris-react/src/components/FooterHelp/FooterHelp.scss +++ b/polaris-react/src/components/FooterHelp/FooterHelp.scss @@ -17,7 +17,5 @@ font-weight: var(--p-font-weight-regular); line-height: var(--p-font-line-height-2); border: none; - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - text-transform: initial; letter-spacing: initial; } diff --git a/polaris-react/src/components/FormLayout/components/Group/Group.tsx b/polaris-react/src/components/FormLayout/components/Group/Group.tsx index b2093e21500..603e10d5576 100644 --- a/polaris-react/src/components/FormLayout/components/Group/Group.tsx +++ b/polaris-react/src/components/FormLayout/components/Group/Group.tsx @@ -1,8 +1,7 @@ -import React, {Children} from 'react'; +import React, {Children, useId} from 'react'; import {classNames} from '../../../../utilities/css'; import {wrapWithComponent} from '../../../../utilities/components'; -import {useUniqueId} from '../../../../utilities/unique-id'; import {Box} from '../../../Box'; import {Item} from '../Item'; import styles from '../../FormLayout.scss'; @@ -17,7 +16,7 @@ export interface GroupProps { export function Group({children, condensed, title, helpText}: GroupProps) { const className = classNames(condensed ? styles.condensed : styles.grouped); - const id = useUniqueId('FormLayoutGroup'); + const id = useId(); let helpTextElement = null; let helpTextID: undefined | string; diff --git a/polaris-react/src/components/Frame/Frame.scss b/polaris-react/src/components/Frame/Frame.scss index 7ca23f15fcc..206c83b70b3 100644 --- a/polaris-react/src/components/Frame/Frame.scss +++ b/polaris-react/src/components/Frame/Frame.scss @@ -300,8 +300,6 @@ font-size: var(--p-font-size-100); font-weight: var(--p-font-weight-medium); line-height: var(--p-font-line-height-1); - // stylelint-disable-next-line -- generated by polaris-migrator DO NOT COPY - text-transform: initial; letter-spacing: initial; color: var(--p-color-text); diff --git a/polaris-react/src/components/Frame/Frame.tsx b/polaris-react/src/components/Frame/Frame.tsx index c5294742a70..547d92fff2c 100644 --- a/polaris-react/src/components/Frame/Frame.tsx +++ b/polaris-react/src/components/Frame/Frame.tsx @@ -137,7 +137,7 @@ class FrameInner extends PureComponent { appear={isNavigationCollapsed} exit={isNavigationCollapsed} in={showMobileNavigation} - timeout={parseInt(motion['duration-300'], 10)} + timeout={parseInt(motion['motion-duration-300'], 10)} classNames={navTransitionClasses} >
; - -export function Default() { - return Online store dashboard; -} diff --git a/polaris-react/src/components/Heading/Heading.tsx b/polaris-react/src/components/Heading/Heading.tsx deleted file mode 100644 index e1fbb916d12..00000000000 --- a/polaris-react/src/components/Heading/Heading.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react'; - -import type {HeadingTagName} from '../../types'; - -import styles from './Heading.scss'; - -export interface HeadingProps { - /** - * The element name to use for the heading - * @default 'h2' - */ - element?: HeadingTagName; - /** The content to display inside the heading */ - children?: React.ReactNode; - /** A unique identifier for the heading, used for reference in anchor links */ - id?: string; -} - -/** - * @deprecated The Heading component will be removed in the next - * major version. Use the Text component instead. See the - * Polaris component guide on how to use Text. - * - * https://polaris.shopify.com/components/text - */ -export function Heading({element: Element = 'h2', children, id}: HeadingProps) { - return ( - - {children} - - ); -} diff --git a/polaris-react/src/components/Heading/index.ts b/polaris-react/src/components/Heading/index.ts deleted file mode 100644 index 6406e7b07f4..00000000000 --- a/polaris-react/src/components/Heading/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Heading'; diff --git a/polaris-react/src/components/Heading/tests/Heading.test.tsx b/polaris-react/src/components/Heading/tests/Heading.test.tsx deleted file mode 100644 index e4851751985..00000000000 --- a/polaris-react/src/components/Heading/tests/Heading.test.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; -import {mountWithApp} from 'tests/utilities'; - -// eslint-disable-next-line import/no-deprecated -import {Heading} from '../Heading'; - -describe('', () => { - it('renders its children', () => { - const text = 'Online store dashboard'; - const heading = mountWithApp({text}); - expect(heading).toContainReactText(text); - }); - - it('renders the specified element', () => { - const heading = mountWithApp( - Online store dashboard, - ); - expect(heading.find('h1')).not.toBeNull(); - }); - - it('renders an h2 element if not specified', () => { - const heading = mountWithApp(Online store dashboard); - expect(heading.find('h2')).not.toBeNull(); - }); - - it('renders an ID for hash links', () => { - const heading = mountWithApp( - - Online store dashboard - , - ); - expect(heading.find('h1')!.props.id).toBe('dashboard'); - }); -}); diff --git a/polaris-react/src/components/IndexFilters/IndexFilters.stories.tsx b/polaris-react/src/components/IndexFilters/IndexFilters.stories.tsx index 8e5997d83b7..e4222b8d930 100644 --- a/polaris-react/src/components/IndexFilters/IndexFilters.stories.tsx +++ b/polaris-react/src/components/IndexFilters/IndexFilters.stories.tsx @@ -1,5 +1,6 @@ import React, {useState, useCallback} from 'react'; import type {ComponentMeta} from '@storybook/react'; +import type {TabProps} from '@shopify/polaris'; import { ChoiceList, Text, @@ -10,7 +11,6 @@ import { TextField, Card, } from '@shopify/polaris'; -import type {AlphaTabProps} from '@shopify/polaris'; import {useSetIndexFiltersMode} from './hooks'; import type {IndexFiltersProps} from './IndexFilters'; @@ -119,7 +119,7 @@ export function Default() { return true; }; - const tabs: AlphaTabProps[] = itemStrings.map((item, index) => ({ + const tabs: TabProps[] = itemStrings.map((item, index) => ({ content: item, index, onAction: () => {}, @@ -326,7 +326,7 @@ export function Default() { } return ( - + + + + Promise; export interface IndexFiltersProps extends Omit< - AlphaFiltersProps, + FiltersProps, 'focused' | 'children' | 'disableQueryField' | 'disableFilters' >, - Pick { + Pick { /** The available sorting choices. If not present, the sort button will not show */ sortOptions?: SortButtonChoice[]; /** The currently selected sort choice. Required if using sorting */ @@ -342,7 +342,7 @@ export function IndexFilters({ ...transitionStyles[state], }} > - (
{mode === IndexFiltersMode.Filtering ? ( - {sortMarkup} - + ) : null}
)} diff --git a/polaris-react/src/components/IndexFilters/tests/IndexFilters.test.tsx b/polaris-react/src/components/IndexFilters/tests/IndexFilters.test.tsx index f272b65d510..3da4e37b9c7 100644 --- a/polaris-react/src/components/IndexFilters/tests/IndexFilters.test.tsx +++ b/polaris-react/src/components/IndexFilters/tests/IndexFilters.test.tsx @@ -3,8 +3,8 @@ import type {ComponentProps} from 'react'; import {mountWithApp} from 'tests/utilities'; import {matchMedia} from '@shopify/jest-dom-mocks'; -import {AlphaTabs} from '../../AlphaTabs'; -import {AlphaFilters} from '../../AlphaFilters'; +import {Tabs} from '../../Tabs'; +import {Filters} from '../../Filters'; import {IndexFilters, IndexFiltersMode} from '..'; import type {IndexFiltersProps} from '../IndexFilters'; import {SearchFilterButton, SortButton, UpdateButtons} from '../components'; @@ -111,7 +111,7 @@ describe('IndexFilters', () => { it('renders non-disabled tabs if the current mode is Default', () => { const wrapper = mountWithApp(); - expect(wrapper).toContainReactComponent(AlphaTabs, { + expect(wrapper).toContainReactComponent(Tabs, { disabled: false, }); }); @@ -119,7 +119,7 @@ describe('IndexFilters', () => { it('overrides and disables tabs even if the current mode is Default', () => { const wrapper = mountWithApp(); - expect(wrapper).toContainReactComponent(AlphaTabs, { + expect(wrapper).toContainReactComponent(Tabs, { disabled: true, }); }); @@ -130,7 +130,7 @@ describe('IndexFilters', () => { ); wrapper.act(() => { - wrapper.find(AlphaFilters)!.trigger('onQueryChange', 'bar'); + wrapper.find(Filters)!.trigger('onQueryChange', 'bar'); }); expect(defaultProps.onQueryChange).toHaveBeenCalledWith('bar'); @@ -165,7 +165,7 @@ describe('IndexFilters', () => { />, ); - expect(wrapper).toContainReactComponent(AlphaFilters, { + expect(wrapper).toContainReactComponent(Filters, { filters, }); }); @@ -191,7 +191,7 @@ describe('IndexFilters', () => { />, ); - expect(wrapper).not.toContainReactComponent(AlphaFilters); + expect(wrapper).not.toContainReactComponent(Filters); }); it('does not render the SortButton or SearchFilterButton component', () => { @@ -275,7 +275,7 @@ describe('IndexFilters', () => { />, ); - expect(wrapper).toContainReactComponent(AlphaFilters, { + expect(wrapper).toContainReactComponent(Filters, { filters, disableFilters: true, disableQueryField: true, diff --git a/polaris-react/src/components/IndexTable/IndexTable.tsx b/polaris-react/src/components/IndexTable/IndexTable.tsx index 260d7137502..a63ec649346 100644 --- a/polaris-react/src/components/IndexTable/IndexTable.tsx +++ b/polaris-react/src/components/IndexTable/IndexTable.tsx @@ -546,7 +546,7 @@ function IndexTableBase({ { - const key = event.keyCode; - const requiredKey = KONAMI_CODE[position.current]; - - if (key === requiredKey) { - if (position.current === KONAMI_CODE.length - 1) { - handler(event); - position.current = 0; - } else { - position.current++; - } - } else { - position.current = 0; - } - }; - - useEffect(() => { - document.addEventListener(keyEvent, handleKeyEvent); - return () => { - document.removeEventListener(keyEvent, handleKeyEvent); - }; - }); - - return null; -} diff --git a/polaris-react/src/components/KonamiCode/index.ts b/polaris-react/src/components/KonamiCode/index.ts deleted file mode 100644 index bcbf369993a..00000000000 --- a/polaris-react/src/components/KonamiCode/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './KonamiCode'; diff --git a/polaris-react/src/components/KonamiCode/tests/KonamiCode.test.tsx b/polaris-react/src/components/KonamiCode/tests/KonamiCode.test.tsx deleted file mode 100644 index 7e848454861..00000000000 --- a/polaris-react/src/components/KonamiCode/tests/KonamiCode.test.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import React from 'react'; -import {mountWithApp} from 'tests/utilities'; - -import {KonamiCode, KONAMI_CODE} from '../KonamiCode'; - -describe('', () => { - beforeEach(() => { - jest.spyOn(console, 'error').mockImplementation(); - }); - - afterEach(() => { - // eslint-disable-next-line no-console - (console.error as jest.Mock).mockRestore(); - }); - - it('calls the handler when the Konami Code is entered', () => { - const spy = jest.fn(); - - mountWithApp(); - simulateKeySequence(KONAMI_CODE); - - expect(spy).toHaveBeenCalledTimes(1); - }); - - it('does not call the handler if the code is wrong', () => { - const spy = jest.fn(); - const reverseKonamiCode = [...KONAMI_CODE].reverse(); - - mountWithApp(); - simulateKeySequence(reverseKonamiCode); - - expect(spy).toHaveBeenCalledTimes(0); - }); - - it('removes Konami Code listener on unmount', () => { - const spy = jest.fn(); - - mountWithApp().unmount(); - simulateKeySequence(KONAMI_CODE); - - expect(spy).toHaveBeenCalledTimes(0); - }); -}); - -function simulateKeySequence(keys: number[]) { - for (const keyCode of keys) { - dispatchKeydown(keyCode); - } -} - -function dispatchKeydown(keyCode: number) { - const event: KeyboardEventInit & {keyCode: number} = {keyCode}; - document.dispatchEvent(new KeyboardEvent('keydown', event)); -} diff --git a/polaris-react/src/components/LegacyCard/LegacyCard.scss b/polaris-react/src/components/LegacyCard/LegacyCard.scss index 7d70f85115f..caac201da35 100644 --- a/polaris-react/src/components/LegacyCard/LegacyCard.scss +++ b/polaris-react/src/components/LegacyCard/LegacyCard.scss @@ -12,14 +12,6 @@ margin-top: calc(-1 * var(--p-space-2)); } } - /* stylelint-disable-next-line polaris/conventions/selector-disallowed-list -- Temporary override, will be removed in v11 */ - + [class^='Polaris-Card'] { - margin-top: var(--p-space-4); - - @media print { - margin-top: calc(-1 * var(--p-space-2)); - } - } @media #{$p-breakpoints-sm-up} { border-radius: var(--p-border-radius-2); diff --git a/polaris-react/src/components/Listbox/Listbox.tsx b/polaris-react/src/components/Listbox/Listbox.tsx index 3da21502150..00b23e0f15b 100644 --- a/polaris-react/src/components/Listbox/Listbox.tsx +++ b/polaris-react/src/components/Listbox/Listbox.tsx @@ -4,13 +4,13 @@ import React, { useEffect, useCallback, useMemo, + useId, Children, } from 'react'; import type {ReactNode} from 'react'; import {debounce} from '../../utilities/debounce'; import {useToggle} from '../../utilities/use-toggle'; -import {useUniqueId} from '../../utilities/unique-id'; import {useComboboxListbox} from '../../utilities/combobox'; import { ListboxContext, @@ -88,7 +88,7 @@ export function Listbox({ setFalse: disableKeyboardEvents, } = useToggle(Boolean(enableKeyboardControl)); - const uniqueId = useUniqueId('Listbox'); + const uniqueId = useId(); const listId = customListId || uniqueId; const scrollableRef = useRef(null); diff --git a/polaris-react/src/components/Listbox/components/Option/Option.tsx b/polaris-react/src/components/Listbox/components/Option/Option.tsx index f991c2e64b6..9beaff172bb 100644 --- a/polaris-react/src/components/Listbox/components/Option/Option.tsx +++ b/polaris-react/src/components/Listbox/components/Option/Option.tsx @@ -1,7 +1,6 @@ -import React, {useRef, useCallback, memo, useContext} from 'react'; +import React, {useRef, useCallback, memo, useContext, useId} from 'react'; import {classNames} from '../../../../utilities/css'; -import {useUniqueId} from '../../../../utilities/unique-id'; import {useListbox} from '../../../../utilities/listbox'; import {useSection, listboxWithinSectionDataSelector} from '../Section'; import {TextOption} from '../TextOption'; @@ -40,7 +39,7 @@ export const Option = memo(function Option({ useContext(MappedActionContext); const listItemRef = useRef(null); - const domId = useUniqueId('ListboxOption'); + const domId = useId(); const sectionId = useSection(); const isWithinSection = Boolean(sectionId); diff --git a/polaris-react/src/components/Listbox/components/Section/Section.tsx b/polaris-react/src/components/Listbox/components/Section/Section.tsx index 4dc74e0e325..d609ea93e32 100644 --- a/polaris-react/src/components/Listbox/components/Section/Section.tsx +++ b/polaris-react/src/components/Listbox/components/Section/Section.tsx @@ -1,8 +1,7 @@ -import React from 'react'; +import React, {useId} from 'react'; import type {ReactNode} from 'react'; import {classNames} from '../../../../utilities/css'; -import {useUniqueId} from '../../../../utilities/unique-id'; import {listboxSectionDataSelector} from './selectors'; import {SectionContext} from './context'; @@ -15,15 +14,15 @@ interface SectionProps { } export function Section({children, divider = true, title}: SectionProps) { - const sectionId = useUniqueId('ListboxSection'); + const id = useId(); return ( - +
  • {title}