- {allowMultiple ? (
-
+ {allowMultiple && !isAction ? (
+
+
+
) : (
children
)}
diff --git a/src/components/Listbox/components/TextOption/tests/TextOption.test.tsx b/src/components/Listbox/components/TextOption/tests/TextOption.test.tsx
index 8e8a8136c19..328793ea5fc 100644
--- a/src/components/Listbox/components/TextOption/tests/TextOption.test.tsx
+++ b/src/components/Listbox/components/TextOption/tests/TextOption.test.tsx
@@ -2,6 +2,7 @@ import React from 'react';
import {mount, mountWithApp} from 'tests/utilities';
import {TextOption} from '../TextOption';
+import {Listbox} from '../../..';
import {Checkbox} from '../../../../Checkbox';
import {ComboboxListboxOptionContext} from '../../../../../utilities/combobox/context';
@@ -34,11 +35,32 @@ describe('TextOption', () => {
child
,
- {
- features: {newDesignLanguage: true},
- },
);
expect(textOption).toContainReactComponent(Checkbox);
});
+
+ it('does not render visual checkbox when allowMultiple is false', () => {
+ const textOption = mountWithApp(
+
+ child
+ ,
+ );
+
+ expect(textOption).not.toContainReactComponent(Checkbox);
+ });
+
+ it('does not render visual checkbox wrapped in a ListBox.Action', () => {
+ const textOption = mountWithApp(
+
+
+
+ child
+
+
+ ,
+ );
+
+ expect(textOption).not.toContainReactComponent(Checkbox);
+ });
});
diff --git a/src/components/Navigation/Navigation.scss b/src/components/Navigation/Navigation.scss
index 7dad19f17e3..7ca74f53e0e 100644
--- a/src/components/Navigation/Navigation.scss
+++ b/src/components/Navigation/Navigation.scss
@@ -56,7 +56,7 @@ $nav-max-width: 360px;
}
@include breakpoint-after(nav-min-window-corrected()) {
- padding-top: var(--p-space-3);
+ padding-top: var(--p-space-4);
}
}
@@ -95,10 +95,9 @@ $disabled-fade: 0.6;
&::before {
content: '';
position: absolute;
- top: 0;
- bottom: 0;
+ top: 1px;
+ bottom: 1px;
left: calc(-1 * var(--p-space-2));
- height: 100%;
width: $active-indicator-width;
background-color: var(--p-action-primary);
border-top-right-radius: var(--p-border-radius-1);
@@ -123,6 +122,10 @@ $disabled-fade: 0.6;
color: var(--p-text-primary);
background-color: var(--p-background-pressed);
}
+
+ @include breakpoint-after(nav-min-window-corrected()) {
+ font-weight: var(--p-font-weight-medium);
+ }
}
.Item-disabled {
@@ -287,14 +290,15 @@ $secondary-item-font-size: 15px;
@include breakpoint-after(nav-min-window-corrected()) {
font-size: $item-font-size-small;
- line-height: 28px;
+ line-height: 1;
+ padding-left: calc(nav(icon-size) + var(--p-space-3));
}
}
.Text {
- margin-top: calc(var(--p-space-1) + var(--p-space-05));
- margin-bottom: calc(var(--p-space-1) + var(--p-space-05));
- line-height: 20px;
+ margin-top: var(--p-space-1);
+ margin-bottom: var(--p-space-1);
+ line-height: var(--p-space-5);
}
.Item-selected {
@@ -335,7 +339,7 @@ $secondary-item-font-size: 15px;
list-style: none;
+ .Section {
- padding-top: var(--p-space-1);
+ padding-top: var(--p-space-2);
padding-bottom: var(--p-space-4);
}
}
@@ -350,11 +354,16 @@ $secondary-item-font-size: 15px;
.SectionHeading {
@include text-style-subheading;
+ text-transform: none; // Adding this intentionally to override the default subheading styling.
display: flex;
align-items: center;
min-height: nav(desktop-nav-height);
padding-left: var(--p-space-4);
+ @include breakpoint-after(nav-min-window-corrected()) {
+ padding-left: var(--p-space-5);
+ }
+
.Text {
font-size: var(--p-font-size-2);
color: var(--p-text-subdued);
@@ -376,6 +385,14 @@ $secondary-item-font-size: 15px;
@include breakpoint-after(nav-min-window-corrected()) {
height: nav(desktop-nav-height);
+
+ // stylelint-disable selector-max-combinators
+ svg,
+ img {
+ height: var(--p-space-4);
+ width: var(--p-space-4);
+ margin: var(--p-space-05);
+ }
}
@include focus-ring;
diff --git a/src/components/Navigation/README.md b/src/components/Navigation/README.md
index 9d4bb4d70ff..70ae876c5bb 100644
--- a/src/components/Navigation/README.md
+++ b/src/components/Navigation/README.md
@@ -113,23 +113,24 @@ The content of the navigation component consists of navigation items. Each item
#### Item properties
-| Prop | Type | Description |
-| ------------------ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
-| url | string | A location for the navigation item to navigate to when clicked |
-| matches | boolean | A boolean property indicating whether the navigation item should respond to a closely matching location property |
-| exactMatch | boolean | A boolean property indicating whether the navigation item should respond to an exactly matching location property |
-| matchPaths | string[] | A string property providing a collection of additional paths for the navigation item to respond to |
-| excludePaths | string[] | A string property providing an explicit collection of paths the navigation item should not respond to |
-| icon | IconProps['source'] | An icon to be displayed next to the navigation item |
-| badge | string \| null | A string property allowing content to be displayed in a badge next to the navigation item |
-| label | string | A string property allowing content to be displayed as link text in the navigation item |
-| disabled | boolean | A boolean property indicating whether the navigation item is disabled |
-| new | boolean | Indicate whether the navigation item is new by adding an indicator dot to the parent and badge to the item (overwritten by the badge prop) |
-| accessibilityLabel | string | A visually hidden label for screen readers to understand the content of a navigation item |
-| selected | boolean | A boolean property indicating whether the navigation item is the currently-selected item |
-| subNavigationItems | SubNavigationItem[] | A collection of navigation items rendered as nested secondary navigation items |
-| secondaryAction | SecondaryAction | Renders an icon-only action as a supplementary action next to a navigation item |
-| onClick() | function | A callback function to handle clicking on a navigation item |
+| Prop | Type | Description |
+| ------------------ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| url | string | A location for the navigation item to navigate to when clicked |
+| matches | boolean | A boolean property indicating whether the navigation item should respond to a closely matching location property |
+| exactMatch | boolean | A boolean property indicating whether the navigation item should respond to an exactly matching location property |
+| matchPaths | string[] | A string property providing a collection of additional paths for the navigation item to respond to |
+| excludePaths | string[] | A string property providing an explicit collection of paths the navigation item should not respond to |
+| icon | IconProps['source'] | An icon to be displayed next to the navigation. Please prefer minor icons here. If a major icon has to be used, set the `shouldResizeIcon` prop to true |
+| badge | string \| null | A string property allowing content to be displayed in a badge next to the navigation item |
+| label | string | A string property allowing content to be displayed as link text in the navigation item |
+| disabled | boolean | A boolean property indicating whether the navigation item is disabled |
+| new | boolean | Indicate whether the navigation item is new by adding an indicator dot to the parent and badge to the item (overwritten by the badge prop) |
+| accessibilityLabel | string | A visually hidden label for screen readers to understand the content of a navigation item |
+| selected | boolean | A boolean property indicating whether the navigation item is the currently-selected item |
+| shouldResizeIcon | boolean | Will allow for major icons to be displayed at the same size as minor icons |
+| subNavigationItems | SubNavigationItem[] | A collection of navigation items rendered as nested secondary navigation items |
+| secondaryAction | SecondaryAction | Renders an icon-only action as a supplementary action next to a navigation item |
+| onClick() | function | A callback function to handle clicking on a navigation item |
@@ -195,7 +196,7 @@ Use to present a navigation menu in the [frame](https://polaris.shopify.com/comp
```
-### Navigation with an active secondary navigation item
+### Navigation with multiple secondary navigations
Use to present a secondary action, related to a section and to title the section.
diff --git a/src/components/Navigation/_variables.scss b/src/components/Navigation/_variables.scss
index a40f8ec4f80..49419e52a7b 100644
--- a/src/components/Navigation/_variables.scss
+++ b/src/components/Navigation/_variables.scss
@@ -65,11 +65,28 @@ $nav-animation-variables: (
font-size: $item-font-size;
font-weight: var(--p-font-weight-semibold);
line-height: $item-line-height-small;
+ padding-left: calc(var(--p-space-4) - var(--p-space-05));
}
&::-moz-focus-inner {
border: 0;
}
+
+ svg,
+ img {
+ display: block;
+ height: var(--p-space-5);
+ width: var(--p-space-5);
+ }
+
+ .Icon-resized {
+ svg,
+ img {
+ margin: var(--p-space-05);
+ height: var(--p-space-4);
+ width: var(--p-space-4);
+ }
+ }
}
@mixin nav-item-icon-attributes {
@@ -96,9 +113,9 @@ $nav-animation-variables: (
margin-right: var(--p-space-4);
margin-bottom: nav(mobile-spacing);
@include breakpoint-after(nav-min-window-corrected()) {
- margin-top: nav(desktop-spacing);
+ margin-top: var(--p-space-4);
margin-right: var(--p-space-4);
- margin-bottom: nav(desktop-spacing);
+ margin-bottom: var(--p-space-4);
}
.Item:hover &,
@@ -113,6 +130,8 @@ $nav-animation-variables: (
.Item-selected:hover &,
.subNavigationActive &,
.subNavigationActive:hover &,
+ .Item-child-active &,
+ .Item-child-active:hover &,
.Item-selected.keyFocused & {
@include recolor-icon(
$fill-color: var(--p-action-primary),
diff --git a/src/components/Navigation/components/Item/Item.tsx b/src/components/Navigation/components/Item/Item.tsx
index 8d20b4a0138..743f3cb7324 100644
--- a/src/components/Navigation/components/Item/Item.tsx
+++ b/src/components/Navigation/components/Item/Item.tsx
@@ -58,6 +58,9 @@ export interface ItemProps extends ItemURLDetails {
subNavigationItems?: SubNavigationItem[];
secondaryAction?: SecondaryAction;
onClick?(): void;
+ onToggleExpandedState?(): void;
+ expanded?: boolean;
+ shouldResizeIcon?: boolean;
}
enum MatchState {
@@ -85,19 +88,21 @@ export function Item({
matchPaths,
excludePaths,
external,
+ onToggleExpandedState,
+ expanded,
+ shouldResizeIcon,
}: ItemProps) {
const i18n = useI18n();
const {isNavigationCollapsed} = useMediaQuery();
const secondaryNavigationId = useUniqueId('SecondaryNavigation');
const {location, onNavigationDismiss} = useContext(NavigationContext);
- const [expanded, setExpanded] = useState(false);
const [keyFocused, setKeyFocused] = useState(false);
useEffect(() => {
if (!isNavigationCollapsed && expanded) {
- setExpanded(false);
+ onToggleExpandedState?.();
}
- }, [expanded, isNavigationCollapsed]);
+ }, [expanded, isNavigationCollapsed, onToggleExpandedState]);
const handleKeyUp = useCallback(
(event) => {
@@ -125,7 +130,12 @@ export function Item({
) : null;
const iconMarkup = icon ? (
-
+
) : null;
@@ -242,11 +252,14 @@ export function Item({
const showExpanded = selected || expanded || childIsActive;
+ const canBeActive = subNavigationItems.length === 0 || !childIsActive;
+
const itemClassName = classNames(
styles.Item,
disabled && styles['Item-disabled'],
- selected && subNavigationItems.length === 0 && styles['Item-selected'],
+ selected && canBeActive && styles['Item-selected'],
showExpanded && styles.subNavigationActive,
+ childIsActive && styles['Item-child-active'],
keyFocused && styles.keyFocused,
);
@@ -340,7 +353,7 @@ export function Item({
isNavigationCollapsed
) {
event.preventDefault();
- setExpanded(!expanded);
+ onToggleExpandedState?.();
} else if (onNavigationDismiss) {
onNavigationDismiss();
if (onClick && onClick !== onNavigationDismiss) {
diff --git a/src/components/Navigation/components/Item/tests/Item.test.tsx b/src/components/Navigation/components/Item/tests/Item.test.tsx
index 076030b8f46..648de24b2f6 100644
--- a/src/components/Navigation/components/Item/tests/Item.test.tsx
+++ b/src/components/Navigation/components/Item/tests/Item.test.tsx
@@ -3,6 +3,8 @@ import {PlusMinor, ExternalMinor} from '@shopify/polaris-icons';
import {matchMedia} from '@shopify/jest-dom-mocks';
import {mountWithApp} from 'tests/utilities';
+import {PolarisTestProvider} from '../../../../PolarisTestProvider';
+import type {MediaQueryContext} from '../../../../../utilities/media-query';
import {Badge} from '../../../../Badge';
import {Icon} from '../../../../Icon';
import {Indicator} from '../../../../Indicator';
@@ -649,6 +651,30 @@ describe('
', () => {
expect(spy).toHaveBeenCalledTimes(1);
});
});
+
+ describe('onToggleExpandedState', () => {
+ it('fires the onToggleExpandedState handler when clicked', () => {
+ const onToggleExpandedState = jest.fn();
+ const item = mountWithNavigationAndPolarisTestProvider(
+
,
+ {location: '/admin/orders'},
+ {isNavigationCollapsed: true},
+ );
+ item?.find('a')?.trigger('onClick', {
+ preventDefault: jest.fn(),
+ currentTarget: {
+ getAttribute: () => 'baz',
+ },
+ });
+ expect(onToggleExpandedState).toHaveBeenCalledTimes(1);
+ });
+ });
});
describe('keyFocused', () => {
@@ -725,3 +751,21 @@ function mountWithNavigationProvider(
,
);
}
+
+function mountWithNavigationAndPolarisTestProvider(
+ node: React.ReactElement,
+ navigationContext: React.ContextType
= {
+ location: '',
+ },
+ mediaQueryContext: React.ContextType = {
+ isNavigationCollapsed: false,
+ },
+) {
+ return mountWithApp(
+
+
+ {node}
+
+ ,
+ );
+}
diff --git a/src/components/Navigation/components/Section/Section.tsx b/src/components/Navigation/components/Section/Section.tsx
index 41ef87e1c6b..8b8b20c6816 100644
--- a/src/components/Navigation/components/Section/Section.tsx
+++ b/src/components/Navigation/components/Section/Section.tsx
@@ -1,8 +1,8 @@
-import React, {useEffect, useRef} from 'react';
+import React, {useEffect, useRef, useState} from 'react';
import {HorizontalDotsMinor} from '@shopify/polaris-icons';
import {classNames} from '../../../../utilities/css';
-import {navigationBarCollapsed} from '../../../../utilities/breakpoints';
+import {useMediaQuery} from '../../../../utilities/media-query';
import {useUniqueId} from '../../../../utilities/unique-id';
import {useToggle} from '../../../../utilities/use-toggle';
import {Collapsible} from '../../../Collapsible';
@@ -43,6 +43,8 @@ export function Section({
setFalse: setExpandedFalse,
} = useToggle(false);
const animationFrame = useRef(null);
+ const {isNavigationCollapsed} = useMediaQuery();
+ const [expandedIndex, setExpandedIndex] = useState();
const handleClick = (
onClick: ItemProps['onClick'],
@@ -57,7 +59,7 @@ export function Section({
cancelAnimationFrame(animationFrame.current);
}
- if (!hasSubNavItems || !navigationBarCollapsed().matches) {
+ if (!hasSubNavItems || !isNavigationCollapsed) {
animationFrame.current = requestAnimationFrame(setExpandedFalse);
}
};
@@ -93,11 +95,19 @@ export function Section({
);
- const itemsMarkup = items.map((item) => {
+ const itemsMarkup = items.map((item, index) => {
const {onClick, label, subNavigationItems, ...rest} = item;
const hasSubNavItems =
subNavigationItems != null && subNavigationItems.length > 0;
+ const handleToggleExpandedState = () => {
+ if (expandedIndex === index) {
+ setExpandedIndex(-1);
+ } else {
+ setExpandedIndex(index);
+ }
+ };
+
return (
);
});
diff --git a/src/components/Navigation/components/Section/tests/Section.test.tsx b/src/components/Navigation/components/Section/tests/Section.test.tsx
index bda4f24106f..7d63638eb63 100644
--- a/src/components/Navigation/components/Section/tests/Section.test.tsx
+++ b/src/components/Navigation/components/Section/tests/Section.test.tsx
@@ -2,6 +2,8 @@ import React from 'react';
import {matchMedia, animationFrame} from '@shopify/jest-dom-mocks';
import {mountWithApp} from 'tests/utilities';
+import {PolarisTestProvider} from '../../../../PolarisTestProvider';
+import type {MediaQueryContext} from '../../../../../utilities/media-query';
import {Collapsible} from '../../../../Collapsible';
import {NavigationContext} from '../../../context';
import {Item} from '../../Item';
@@ -270,6 +272,88 @@ describe('', () => {
expect(onClickSpy).toHaveBeenCalledTimes(1);
});
+
+ it('acts as an accordion when on a mobile viewport', () => {
+ matchMedia.setMedia(() => ({matches: true}));
+ const withSubNav = mountWithNavigationAndPolarisTestProvider(
+ ,
+ {
+ ...context,
+ },
+ {
+ isNavigationCollapsed: true,
+ },
+ );
+ const firstItem = withSubNav.find(Item, {label: 'label a'});
+ const secondItem = withSubNav.find(Item, {label: 'label b'});
+ const thirdItem = withSubNav.find(Item, {label: 'label c'});
+
+ firstItem?.trigger('onToggleExpandedState');
+ expect(withSubNav.find(Item, {label: 'label a'})).toHaveReactProps({
+ expanded: true,
+ });
+
+ secondItem?.trigger('onToggleExpandedState');
+ expect(withSubNav.find(Item, {label: 'label a'})).toHaveReactProps({
+ expanded: false,
+ });
+ expect(withSubNav.find(Item, {label: 'label b'})).toHaveReactProps({
+ expanded: true,
+ });
+
+ thirdItem?.trigger('onToggleExpandedState');
+ expect(withSubNav.find(Item, {label: 'label b'})).toHaveReactProps({
+ expanded: false,
+ });
+ expect(withSubNav.find(Item, {label: 'label c'})).toHaveReactProps({
+ expanded: true,
+ });
+ });
});
function mountWithNavigationProvider(
@@ -283,4 +367,22 @@ function mountWithNavigationProvider(
);
}
+function mountWithNavigationAndPolarisTestProvider(
+ node: React.ReactElement,
+ navigationContext: React.ContextType = {
+ location: '',
+ },
+ mediaQueryContext: React.ContextType = {
+ isNavigationCollapsed: false,
+ },
+) {
+ return mountWithApp(
+
+
+ {node}
+
+ ,
+ );
+}
+
function noop() {}
diff --git a/src/components/Page/Page.scss b/src/components/Page/Page.scss
index 66a2d295b5e..4217aaaf855 100644
--- a/src/components/Page/Page.scss
+++ b/src/components/Page/Page.scss
@@ -37,6 +37,6 @@ body {
}
.divider {
- border-top: var(--p-border-base);
+ border-top: var(--p-border-divider);
padding-top: var(--p-space-4);
}
diff --git a/src/components/Page/Page.tsx b/src/components/Page/Page.tsx
index f7bfab90e7d..cfda646ca70 100644
--- a/src/components/Page/Page.tsx
+++ b/src/components/Page/Page.tsx
@@ -37,7 +37,7 @@ export function Page({
(rest.breadcrumbs != null && rest.breadcrumbs.length > 0);
const contentClassName = classNames(
- styles.Content,
+ !hasHeaderContent && styles.Content,
divider && hasHeaderContent && styles.divider,
);
diff --git a/src/components/Page/README.md b/src/components/Page/README.md
index 0f468bc559b..369cd82c763 100644
--- a/src/components/Page/README.md
+++ b/src/components/Page/README.md
@@ -169,7 +169,9 @@ Use for detail pages, which should have pagination and breadcrumbs, and also oft
}}
additionalNavigation={}
>
- Page content
+
+ Credit card information
+
```
@@ -217,7 +219,9 @@ Use to create a custom primary action.
}
>
- Page content
+
+ Credit card information
+
```
@@ -252,6 +256,21 @@ Use when a primary action functions better as part of the page content instead o
```
+### Page with destructive secondary action
+
+
+
+Used to visually indicate that the secondary page action is destructive.
+
+```jsx
+
+ Page content
+
+```
+
### Page with subtitle
@@ -265,7 +284,9 @@ Use when the page title benefits from secondary content.
subtitle="Statement period: May 3, 2019 to June 2, 2019"
secondaryActions={[{content: 'Download', icon: ArrowDownMinor}]}
>
- Page content
+
+ Credit card information
+
```
@@ -288,7 +309,9 @@ Use when a secondary action links to another website. Actions marked external op
},
]}
>
- Page Content
+
+ Credit card information
+
```
@@ -304,7 +327,9 @@ Use when the page doesn’t represent a list of objects or a detail view for an
title="General"
primaryAction={{content: 'Save'}}
>
- Page content
+
+ Credit card information
+
```
@@ -324,7 +349,9 @@ Use for layouts that benefit from more screen width, such as wide tables or list
hasNext: true,
}}
>
- Wide page content
+
+ Credit card information
+
```
@@ -372,7 +399,9 @@ Use action groups for sets of actions that relate to one another, particularly w
},
]}
>
- Page content
+
+ Credit card information
+
```
@@ -394,7 +423,9 @@ Title metadata appears immediately after the page’s title. Use it to communica
hasNext: true,
}}
>
- Page content
+
+ Credit card information
+
```
@@ -410,7 +441,9 @@ Use when the page needs visual separation between the page header and the conten
title="General"
divider
>
- Page content
+
+ Credit card information
+
```
diff --git a/src/components/Page/components/Header/Header.scss b/src/components/Page/components/Header/Header.scss
index cc07864d185..8e804980861 100644
--- a/src/components/Page/components/Header/Header.scss
+++ b/src/components/Page/components/Header/Header.scss
@@ -161,7 +161,10 @@ $action-menu-rollup-computed-width: 40px;
.Row {
display: flex;
justify-content: space-between;
- min-height: 36px;
+
+ &:first-child {
+ min-height: 36px;
+ }
+ .Row {
margin-top: var(--p-space-1);
@@ -262,6 +265,9 @@ $action-menu-rollup-computed-width: 40px;
// stylelint-disable-next-line selector-max-class
.RightAlign {
margin-bottom: var(--p-space-1);
+ @include breakpoint-after(layout-width(page-with-nav)) {
+ margin-bottom: 0;
+ }
}
// stylelint-disable-next-line selector-max-class
.Row {
diff --git a/src/components/Page/components/Header/components/Title/Title.scss b/src/components/Page/components/Header/components/Title/Title.scss
index 04426871ab7..99099a70f9d 100644
--- a/src/components/Page/components/Header/components/Title/Title.scss
+++ b/src/components/Page/components/Header/components/Title/Title.scss
@@ -11,8 +11,12 @@
}
}
+.TitleWithSubtitle {
+ margin-top: var(--p-space-1);
+}
+
.SubTitle {
- margin-top: var(--p-space-2);
+ margin-top: var(--p-space-1);
color: var(--p-text-subdued);
&.SubtitleCompact {
diff --git a/src/components/Page/components/Header/components/Title/Title.tsx b/src/components/Page/components/Header/components/Title/Title.tsx
index acb41cc8f0a..7f465528dc1 100644
--- a/src/components/Page/components/Header/components/Title/Title.tsx
+++ b/src/components/Page/components/Header/components/Title/Title.tsx
@@ -21,7 +21,12 @@ export function Title({
titleMetadata,
compactTitle,
}: TitleProps) {
- const titleMarkup = title ? {title}
: null;
+ const className = classNames(
+ styles.Title,
+ subtitle && styles.TitleWithSubtitle,
+ );
+
+ const titleMarkup = title ? {title}
: null;
const titleMetadataMarkup = titleMetadata ? (
{titleMetadata}
diff --git a/src/components/Page/tests/Page.test.tsx b/src/components/Page/tests/Page.test.tsx
index 652eb76f494..5f1340f0790 100644
--- a/src/components/Page/tests/Page.test.tsx
+++ b/src/components/Page/tests/Page.test.tsx
@@ -253,7 +253,7 @@ describe('', () => {
it('renders border when divider is true and header props exist', () => {
const wrapper = mountWithApp();
expect(wrapper).toContainReactComponent('div', {
- className: 'Content divider',
+ className: 'divider',
});
});
@@ -270,10 +270,7 @@ describe('', () => {
it('does not render border when divider is false', () => {
const wrapper = mountWithApp();
expect(wrapper).not.toContainReactComponent('div', {
- className: 'Content divider',
- });
- expect(wrapper).toContainReactComponent('div', {
- className: 'Content',
+ className: 'divider',
});
});
});
diff --git a/src/components/README.md b/src/components/README.md
index 179768eda8d..810d4ae2a94 100644
--- a/src/components/README.md
+++ b/src/components/README.md
@@ -66,7 +66,7 @@ Include the CSS in your HTML. We suggest copying the styles file into your own p
```html
```
@@ -100,7 +100,7 @@ Include the CSS stylesheet in your HTML. We suggest copying the styles file into
```html
```
@@ -108,7 +108,7 @@ Polaris components rely on CSS custom properties generated by the ThemeProvider.
```html
```
diff --git a/src/components/ResourceList/ResourceList.tsx b/src/components/ResourceList/ResourceList.tsx
index 74b8b59795a..0cb9300cac9 100644
--- a/src/components/ResourceList/ResourceList.tsx
+++ b/src/components/ResourceList/ResourceList.tsx
@@ -736,6 +736,3 @@ export const ResourceList: ResourceListType = function ResourceList({
};
ResourceList.Item = ResourceItem;
-
-export {FilterControl} from './components';
-export type {FilterControlProps} from './components';
diff --git a/src/components/ResourceList/components/FilterControl/FilterControl.scss b/src/components/ResourceList/components/FilterControl/FilterControl.scss
deleted file mode 100644
index c301669692e..00000000000
--- a/src/components/ResourceList/components/FilterControl/FilterControl.scss
+++ /dev/null
@@ -1,50 +0,0 @@
-@import '../../../../styles/common';
-@import '../../variables';
-
-.AppliedFilters {
- list-style-type: none;
- padding: 0;
- margin: 0;
- display: flex;
- flex-wrap: nowrap;
- margin-top: calc(resource-list(header-padding-small) * -1);
- margin-left: calc(resource-list(header-padding-small) * -1);
- margin-right: calc(resource-list(header-padding-small) * -1);
- overflow-x: auto;
- overflow-y: hidden;
- -webkit-overflow-scrolling: touch;
-
- &::after {
- content: '';
- flex: 0 0 resource-list(header-padding-small);
- }
-
- @include breakpoint-after(resource-list(breakpoint-small)) {
- flex-wrap: wrap;
- margin-right: 0;
- margin-left: calc(var(--p-space-2) * -1);
- margin-top: calc(var(--p-space-4) * -1);
-
- &::after {
- content: none;
- flex: none;
- }
- }
-
- @include breakpoint-before(resource-list(breakpoint-small)) {
- // account for scrollbar
- padding-bottom: var(--p-space-2);
- }
-}
-
-.AppliedFilter {
- flex: 1 0 auto;
- margin-top: var(--p-space-2);
- margin-left: resource-list(header-padding-small);
- max-width: calc(100% - var(--p-space-2));
-
- @include breakpoint-after(resource-list(breakpoint-small)) {
- flex: 0 1 auto;
- margin-left: var(--p-space-2);
- }
-}
diff --git a/src/components/ResourceList/components/FilterControl/FilterControl.tsx b/src/components/ResourceList/components/FilterControl/FilterControl.tsx
deleted file mode 100644
index b32f2ceb95d..00000000000
--- a/src/components/ResourceList/components/FilterControl/FilterControl.tsx
+++ /dev/null
@@ -1,291 +0,0 @@
-import React, {useCallback, useContext} from 'react';
-import {SearchMinor} from '@shopify/polaris-icons';
-
-import type {ComplexAction} from '../../../../types';
-import {buttonsFrom} from '../../../Button';
-import {Icon} from '../../../Icon';
-import {FormLayout} from '../../../FormLayout';
-import {TextField} from '../../../TextField';
-import {Tag} from '../../../Tag';
-import {useI18n} from '../../../../utilities/i18n';
-import {ResourceListContext} from '../../../../utilities/resource-list';
-
-import {FilterCreator} from './components';
-import {AppliedFilter, Filter, FilterType, Operator} from './types';
-import styles from './FilterControl.scss';
-
-export interface FilterControlProps {
- searchValue?: string;
- appliedFilters?: AppliedFilter[];
- additionalAction?: ComplexAction;
- focused?: boolean;
- filters?: Filter[];
- placeholder?: string;
- onSearchBlur?(): void;
- onSearchChange(searchValue: string, id: string): void;
- onFiltersChange?(appliedFilters: AppliedFilter[]): void;
-}
-
-/** @deprecated This is a private component, do not use it. This component might be removed in a minor version update. Use instead. */
-export function FilterControl({
- searchValue,
- appliedFilters = [],
- additionalAction,
- focused = false,
- filters = [],
- placeholder,
- onSearchBlur,
- onSearchChange,
- onFiltersChange,
-}: FilterControlProps) {
- if (process.env.NODE_ENV === 'development') {
- // eslint-disable-next-line no-console
- console.warn(
- 'Deprecation: is deprecated. This is a private component, do not use it. This component might be removed in a minor version update. Use instead.',
- );
- }
-
- const i18n = useI18n();
- const {selectMode, resourceName} = useContext(ResourceListContext);
-
- const filterResourceName = resourceName || {
- singular: i18n.translate('Polaris.ResourceList.defaultItemSingular'),
- plural: i18n.translate('Polaris.ResourceList.defaultItemPlural'),
- };
-
- const handleAddFilter = useCallback(
- (newFilter: AppliedFilter) => {
- if (!onFiltersChange) {
- return;
- }
-
- const foundFilter = appliedFilters.find(
- (appliedFilter) =>
- idFromFilter(appliedFilter) === idFromFilter(newFilter),
- );
-
- if (foundFilter) {
- return;
- }
-
- const newAppliedFilters = [...appliedFilters, newFilter];
-
- onFiltersChange(newAppliedFilters);
- },
- [onFiltersChange, appliedFilters],
- );
-
- const handleRemoveFilter = useCallback(
- (filterId: string) => {
- if (!onFiltersChange) {
- return;
- }
-
- const foundIndex = appliedFilters.findIndex(
- (appliedFilter) => idFromFilter(appliedFilter) === filterId,
- );
-
- const newAppliedFilters =
- foundIndex >= 0
- ? [
- ...appliedFilters.slice(0, foundIndex),
- ...appliedFilters.slice(foundIndex + 1, appliedFilters.length),
- ]
- : [...appliedFilters];
-
- onFiltersChange(newAppliedFilters);
- },
- [appliedFilters, onFiltersChange],
- );
-
- const getRemoveFilterCallback = useCallback(
- (filterId: string) => {
- return () => {
- handleRemoveFilter(filterId);
- };
- },
- [handleRemoveFilter],
- );
-
- const textFieldLabel = placeholder
- ? placeholder
- : i18n.translate('Polaris.ResourceList.FilterControl.textFieldLabel', {
- resourceNamePlural: filterResourceName.plural.toLocaleLowerCase(),
- });
-
- if (additionalAction) {
- additionalAction.disabled = selectMode;
- }
-
- const additionalActionButton =
- (additionalAction && buttonsFrom(additionalAction)) || null;
-
- const filterCreatorMarkup =
- filters.length > 0 ? (
-
- ) : null;
-
- const appliedFiltersMarkup = appliedFilters.map((appliedFilter) => {
- const activeFilterLabel = getFilterLabel(appliedFilter);
- const filterId = idFromFilter(appliedFilter);
- return (
-
-
- {activeFilterLabel}
-
-
- );
- });
-
- const appliedFiltersWrapper =
- appliedFilters.length > 0 ? (
-
- ) : null;
-
- return (
-
- }
- value={searchValue}
- onChange={onSearchChange}
- onBlur={onSearchBlur}
- focused={focused}
- disabled={selectMode}
- autoComplete="off"
- />
- {appliedFiltersWrapper}
-
- );
-
- function getFilterLabel(appliedFilter: AppliedFilter) {
- const {key, value, label} = appliedFilter;
- if (label) {
- return label;
- }
-
- const filter = filters.find((filter: any) => {
- const {minKey, maxKey, operatorText} = filter;
-
- if (minKey || maxKey) {
- return filter.key === key || minKey === key || maxKey === key;
- }
-
- if (operatorText && typeof operatorText !== 'string') {
- return (
- filter.key === key ||
- operatorText.filter(
- ({key: operatorKey}: Operator) => operatorKey === key,
- ).length === 1
- );
- }
-
- return filter.key === key;
- });
-
- if (!filter) {
- return value;
- }
-
- const filterOperatorLabel = findOperatorLabel(filter, appliedFilter);
- const filterLabelByType = findFilterLabelByType(filter, appliedFilter);
-
- if (!filterOperatorLabel) {
- return `${filter.label} ${filterLabelByType}`;
- }
-
- return `${filter.label} ${filterOperatorLabel} ${filterLabelByType}`;
- }
-
- function findFilterLabelByType(filter: Filter, appliedFilter: AppliedFilter) {
- const {value: appliedFilterValue} = appliedFilter;
-
- if (filter.type === FilterType.Select) {
- const foundFilterOption = filter.options.find((option) =>
- typeof option === 'string'
- ? option === appliedFilterValue
- : option.value === appliedFilterValue,
- );
-
- if (foundFilterOption) {
- return typeof foundFilterOption === 'string'
- ? foundFilterOption
- : foundFilterOption.label;
- }
- }
-
- if (filter.type === FilterType.DateSelector) {
- if (filter.key === appliedFilter.key) {
- const filterLabelKey = `Polaris.ResourceList.DateSelector.FilterLabelForValue.${appliedFilter.value}`;
-
- return i18n.translationKeyExists(filterLabelKey)
- ? i18n.translate(filterLabelKey)
- : appliedFilter.value;
- }
-
- if (appliedFilter.key === filter.maxKey) {
- return i18n.translate(
- 'Polaris.ResourceList.DateSelector.FilterLabelForValue.on_or_before',
- {
- date: formatDateForLabelDisplay(appliedFilter.value),
- },
- );
- }
-
- if (appliedFilter.key === filter.minKey) {
- return i18n.translate(
- 'Polaris.ResourceList.DateSelector.FilterLabelForValue.on_or_after',
- {
- date: formatDateForLabelDisplay(appliedFilter.value),
- },
- );
- }
- }
-
- return appliedFilterValue;
- }
-}
-
-function idFromFilter(appliedFilter: AppliedFilter) {
- return `${appliedFilter.key}-${appliedFilter.value}`;
-}
-
-function formatDateForLabelDisplay(date: string) {
- if (isNaN(new Date(date).getTime())) {
- return date;
- }
-
- return new Date(date.replace(/-/g, '/')).toLocaleDateString();
-}
-
-function findOperatorLabel(filter: Filter, appliedFilter: AppliedFilter) {
- const {operatorText} = filter;
-
- if (
- filter.type === FilterType.DateSelector &&
- (appliedFilter.key === filter.minKey || appliedFilter.key === filter.maxKey)
- ) {
- return '';
- }
-
- if (!operatorText || typeof operatorText === 'string') {
- return operatorText;
- }
-
- const appliedOperator = operatorText.find((operator) => {
- return operator.key === appliedFilter.key;
- });
-
- if (appliedOperator) {
- return appliedOperator.filterLabel || appliedOperator.optionLabel;
- }
-}
diff --git a/src/components/ResourceList/components/FilterControl/components/DateSelector/DateSelector.scss b/src/components/ResourceList/components/FilterControl/components/DateSelector/DateSelector.scss
deleted file mode 100644
index f12865310a9..00000000000
--- a/src/components/ResourceList/components/FilterControl/components/DateSelector/DateSelector.scss
+++ /dev/null
@@ -1,9 +0,0 @@
-@import '../../../../../../styles/common';
-
-.DateTextField {
- margin-top: var(--p-space-2);
-}
-
-.DatePicker {
- margin-top: var(--p-space-4);
-}
diff --git a/src/components/ResourceList/components/FilterControl/components/DateSelector/DateSelector.tsx b/src/components/ResourceList/components/FilterControl/components/DateSelector/DateSelector.tsx
deleted file mode 100644
index e4f99bf8dc3..00000000000
--- a/src/components/ResourceList/components/FilterControl/components/DateSelector/DateSelector.tsx
+++ /dev/null
@@ -1,389 +0,0 @@
-import React, {useState, useCallback, useRef, memo} from 'react';
-import {CalendarMinor} from '@shopify/polaris-icons';
-
-import {DatePicker, Range} from '../../../../../DatePicker';
-import {Select} from '../../../../../Select';
-import {TextField} from '../../../../../TextField';
-import {Icon} from '../../../../../Icon';
-import {useI18n} from '../../../../../../utilities/i18n';
-
-import styles from './DateSelector.scss';
-
-const VALID_DATE_REGEX = /^\d{4}-\d{1,2}-\d{1,2}$/;
-
-type DateOptionType = 'past' | 'future' | 'full';
-
-export interface DateSelectorProps {
- dateOptionType?: DateOptionType;
- filterValue?: string;
- filterKey?: string;
- filterMinKey: string;
- filterMaxKey: string;
- onFilterValueChange(filterValue?: string): void;
- onFilterKeyChange(filterKey?: string): void;
-}
-
-export enum DateFilterOption {
- PastWeek = 'past_week',
- PastMonth = 'past_month',
- PastQuarter = 'past_quarter',
- PastYear = 'past_year',
- ComingWeek = 'coming_week',
- ComingMonth = 'coming_month',
- ComingQuarter = 'coming_quarter',
- ComingYear = 'coming_year',
- OnOrBefore = 'on_or_before',
- OnOrAfter = 'on_or_after',
-}
-
-export const DateSelector = memo(function DateSelector({
- filterValue,
- filterKey,
- filterMinKey,
- filterMaxKey,
- dateOptionType,
- onFilterValueChange,
- onFilterKeyChange,
-}: DateSelectorProps) {
- const now = new Date();
-
- const i18n = useI18n();
- const initialConsumerFilterKey = useRef(filterKey);
- const [datePickerMonth, setDatePickerMonth] = useState(now.getMonth());
- const [datePickerYear, setDatePickerYear] = useState(now.getFullYear());
- const [selectedDate, setSelectedDate] = useState();
- const [userInputDate, setUserInputDate] = useState();
- const [userInputDateError, setUserInputDateError] = useState();
-
- const dateTextFieldValue = getDateTextFieldValue();
-
- const handleDateFieldChange = useCallback(
- (value: string) => {
- if (value.length === 0) {
- setSelectedDate(undefined);
- onFilterValueChange(undefined);
- }
-
- if (userInputDateError && isValidDate(value)) {
- setUserInputDateError(undefined);
- }
-
- setUserInputDate(value);
- },
- [onFilterValueChange, userInputDateError],
- );
-
- const handleDateChanged = useCallback(
- (date) => {
- if (!date) {
- return;
- }
- onFilterValueChange(
- stripTimeFromISOString(formatDateForLocalTimezone(date)),
- );
- },
- [onFilterValueChange],
- );
-
- const handleDateBlur = useCallback(() => {
- if (!dateTextFieldValue || !isValidDate(dateTextFieldValue)) {
- setSelectedDate(undefined);
- setUserInputDateError(
- i18n.translate('Polaris.ResourceList.DateSelector.dateValueError'),
- );
- onFilterValueChange(undefined);
-
- return;
- }
-
- if (!userInputDate) {
- return;
- }
-
- const formattedDateForTimezone = new Date(
- formatDateForLocalTimezone(new Date(userInputDate)),
- );
-
- setSelectedDate(formattedDateForTimezone);
- setDatePickerMonth(formattedDateForTimezone.getMonth());
- setDatePickerYear(formattedDateForTimezone.getFullYear());
- setUserInputDate(undefined);
- setUserInputDateError(undefined);
-
- handleDateChanged(formattedDateForTimezone);
- }, [
- dateTextFieldValue,
- handleDateChanged,
- i18n,
- onFilterValueChange,
- userInputDate,
- ]);
-
- const handleDateFilterOptionsChange = useCallback(
- (newOption: string) => {
- if (!initialConsumerFilterKey.current) {
- return;
- }
-
- if (newOption === DateFilterOption.OnOrBefore) {
- onFilterKeyChange(filterMaxKey);
- onFilterValueChange(
- selectedDate
- ? stripTimeFromISOString(formatDateForLocalTimezone(selectedDate))
- : undefined,
- );
- return;
- }
-
- if (newOption === DateFilterOption.OnOrAfter) {
- onFilterKeyChange(filterMinKey);
- onFilterValueChange(
- selectedDate
- ? stripTimeFromISOString(formatDateForLocalTimezone(selectedDate))
- : undefined,
- );
- return;
- }
-
- onFilterKeyChange(initialConsumerFilterKey.current);
- onFilterValueChange(newOption);
- },
- [
- filterMaxKey,
- filterMinKey,
- initialConsumerFilterKey,
- onFilterKeyChange,
- onFilterValueChange,
- selectedDate,
- ],
- );
-
- const handleDatePickerChange = useCallback(
- ({end: nextDate}: Range) => {
- const date = new Date(nextDate);
- setSelectedDate(date);
- setUserInputDate(undefined);
- setUserInputDateError(undefined);
-
- handleDateChanged(date);
- },
- [handleDateChanged],
- );
-
- const handleDatePickerMonthChange = useCallback(
- (month: number, year: number) => {
- setDatePickerMonth(month);
- setDatePickerYear(year);
- },
- [],
- );
-
- const dateFilterOption = getDateFilterOption(
- filterValue,
- filterKey,
- filterMinKey,
- filterMaxKey,
- );
-
- const showDatePredicate =
- dateFilterOption === DateFilterOption.OnOrBefore ||
- dateFilterOption === DateFilterOption.OnOrAfter;
-
- const datePredicateMarkup = showDatePredicate && (
- <>
-
- }
- autoComplete="off"
- onChange={handleDateFieldChange}
- onBlur={handleDateBlur}
- />
-
-
-
-
- >
- );
-
- const dateOptionTypes = {
- past: [...getDatePastOptions(), ...getDateComparatorOptions()],
- future: [...getDateFutureOptions(), ...getDateComparatorOptions()],
- full: [
- ...getDatePastOptions(),
- ...getDateFutureOptions(),
- ...getDateComparatorOptions(),
- ],
- };
-
- return (
- <>
-
- {datePredicateMarkup}
- >
- );
-
- function getDateComparatorOptions() {
- return [
- {
- value: DateFilterOption.OnOrBefore,
- label: i18n.translate(
- 'Polaris.ResourceList.DateSelector.SelectOptions.OnOrBefore',
- ),
- },
- {
- value: DateFilterOption.OnOrAfter,
- label: i18n.translate(
- 'Polaris.ResourceList.DateSelector.SelectOptions.OnOrAfter',
- ),
- },
- ];
- }
-
- function getDatePastOptions() {
- return [
- {
- value: DateFilterOption.PastWeek,
- label: i18n.translate(
- 'Polaris.ResourceList.DateSelector.SelectOptions.PastWeek',
- ),
- },
- {
- value: DateFilterOption.PastMonth,
- label: i18n.translate(
- 'Polaris.ResourceList.DateSelector.SelectOptions.PastMonth',
- ),
- },
- {
- value: DateFilterOption.PastQuarter,
- label: i18n.translate(
- 'Polaris.ResourceList.DateSelector.SelectOptions.PastQuarter',
- ),
- },
- {
- value: DateFilterOption.PastYear,
- label: i18n.translate(
- 'Polaris.ResourceList.DateSelector.SelectOptions.PastYear',
- ),
- },
- ];
- }
-
- function getDateFutureOptions() {
- return [
- {
- value: DateFilterOption.ComingWeek,
- label: i18n.translate(
- 'Polaris.ResourceList.DateSelector.SelectOptions.ComingWeek',
- ),
- },
- {
- value: DateFilterOption.ComingMonth,
- label: i18n.translate(
- 'Polaris.ResourceList.DateSelector.SelectOptions.ComingMonth',
- ),
- },
- {
- value: DateFilterOption.ComingQuarter,
- label: i18n.translate(
- 'Polaris.ResourceList.DateSelector.SelectOptions.ComingQuarter',
- ),
- },
- {
- value: DateFilterOption.ComingYear,
- label: i18n.translate(
- 'Polaris.ResourceList.DateSelector.SelectOptions.ComingYear',
- ),
- },
- ];
- }
-
- function getDateTextFieldValue() {
- if (!userInputDate && !selectedDate) {
- return undefined;
- }
-
- if (userInputDate !== undefined) {
- return userInputDate;
- }
-
- if (selectedDate) {
- return stripTimeFromISOString(formatDateForLocalTimezone(selectedDate));
- }
- }
-});
-
-function isValidDate(date?: string) {
- if (!date) {
- return false;
- }
- return VALID_DATE_REGEX.test(date) && !isNaN(new Date(date).getTime());
-}
-
-function getDateFilterOption(
- filterValue?: string,
- filterKey?: string,
- filterMinKey?: string,
- filterMaxKey?: string,
-) {
- if (filterKey === filterMaxKey) {
- return DateFilterOption.OnOrBefore;
- }
-
- if (filterKey === filterMinKey) {
- return DateFilterOption.OnOrAfter;
- }
-
- return filterValue;
-}
-
-function stripTimeFromISOString(ISOString: string) {
- return ISOString.slice(0, 10);
-}
-
-function formatDateForLocalTimezone(date: Date) {
- const timezoneOffset = date.getTimezoneOffset();
- const timezoneOffsetMs = timezoneOffset * 60 * 1000;
- const isFringeTimezone = timezoneOffset === -720 || timezoneOffset === 720;
- const formattedDate = new Date();
-
- if (isFringeTimezone && date.getHours() !== 0) {
- return date.toISOString();
- }
-
- const newTime =
- timezoneOffset > -1
- ? date.getTime() + timezoneOffsetMs
- : date.getTime() - timezoneOffsetMs;
-
- formattedDate.setTime(newTime);
- return formattedDate.toISOString();
-}
diff --git a/src/components/ResourceList/components/FilterControl/components/DateSelector/index.ts b/src/components/ResourceList/components/FilterControl/components/DateSelector/index.ts
deleted file mode 100644
index 898044cd1f5..00000000000
--- a/src/components/ResourceList/components/FilterControl/components/DateSelector/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './DateSelector';
diff --git a/src/components/ResourceList/components/FilterControl/components/DateSelector/tests/DateSelector.test.tsx b/src/components/ResourceList/components/FilterControl/components/DateSelector/tests/DateSelector.test.tsx
deleted file mode 100644
index 5d72656242a..00000000000
--- a/src/components/ResourceList/components/FilterControl/components/DateSelector/tests/DateSelector.test.tsx
+++ /dev/null
@@ -1,587 +0,0 @@
-import React from 'react';
-import {mountWithApp} from 'tests/utilities';
-
-import {DatePicker} from '../../../../../../DatePicker';
-import {Select} from '../../../../../../Select';
-import {TextField} from '../../../../../../TextField';
-import {
- DateSelector,
- DateSelectorProps,
- DateFilterOption,
-} from '../DateSelector';
-
-describe('', () => {
- const mockDefaultProps: DateSelectorProps = {
- filterKey: 'starts',
- filterMinKey: 'starts_min',
- filterMaxKey: 'starts_max',
- onFilterValueChange: noop,
- onFilterKeyChange: noop,
- };
-
- const dateOptionType = {
- past: [
- DateFilterOption.PastWeek,
- DateFilterOption.PastMonth,
- DateFilterOption.PastQuarter,
- DateFilterOption.PastYear,
- DateFilterOption.OnOrBefore,
- DateFilterOption.OnOrAfter,
- ],
- future: [
- DateFilterOption.ComingWeek,
- DateFilterOption.ComingMonth,
- DateFilterOption.ComingQuarter,
- DateFilterOption.ComingYear,
- DateFilterOption.OnOrBefore,
- DateFilterOption.OnOrAfter,
- ],
- full: [
- DateFilterOption.PastWeek,
- DateFilterOption.PastMonth,
- DateFilterOption.PastQuarter,
- DateFilterOption.PastYear,
- DateFilterOption.ComingWeek,
- DateFilterOption.ComingMonth,
- DateFilterOption.ComingQuarter,
- DateFilterOption.ComingYear,
- DateFilterOption.OnOrBefore,
- DateFilterOption.OnOrAfter,
- ],
- };
-
- let getTimezoneOffset: jest.SpyInstance;
-
- beforeEach(() => {
- getTimezoneOffset = jest.spyOn(Date.prototype, 'getTimezoneOffset');
- getTimezoneOffset.mockImplementation(() => -540);
- });
-
- afterEach(() => {
- getTimezoneOffset.mockRestore();
- });
-
- describe('dateOptionType', () => {
- it('builds date filters Select options for past option type', () => {
- const wrapper = mountWithApp(
- ,
- );
-
- const expectOptionValues = dateOptionType.past;
-
- expect(
- getOptionsValuesList(wrapper.find(Select)!.prop('options')),
- ).toStrictEqual(expectOptionValues);
- });
-
- it('builds date filters Select options for future option type', () => {
- const wrapper = mountWithApp(
- ,
- );
-
- const expectOptionValues = dateOptionType.future;
-
- expect(
- getOptionsValuesList(wrapper.find(Select)!.prop('options')),
- ).toStrictEqual(expectOptionValues);
- });
-
- it('builds date filters Select options for full option type', () => {
- const wrapper = mountWithApp(
- ,
- );
-
- const expectOptionValues = dateOptionType.full;
-
- expect(
- getOptionsValuesList(wrapper.find(Select)!.prop('options')),
- ).toStrictEqual(expectOptionValues);
- });
-
- it('defaults to full date filters Select options when option type is missing', () => {
- const wrapper = mountWithApp();
-
- const expectOptionValues = dateOptionType.full;
-
- expect(
- getOptionsValuesList(wrapper.find(Select)!.prop('options')),
- ).toStrictEqual(expectOptionValues);
- });
- });
-
- describe('filterValue', () => {
- it('sets option in date filters Select', () => {
- const dateFilterValue = DateFilterOption.PastMonth;
-
- const wrapper = mountWithApp(
- ,
- );
-
- expect(wrapper).toContainReactComponent(Select, {
- value: dateFilterValue,
- });
- });
-
- it('displays DatePicker when filterValue is filter with minimum date predicate (on or after)', () => {
- const dateFilterValue = DateFilterOption.OnOrAfter;
-
- const wrapper = mountWithApp(
- ,
- );
-
- expect(wrapper).toContainReactComponent(DatePicker);
- });
-
- it('displays DatePicker when filterValue is filter with maximum date predicate (on or before)', () => {
- const dateFilterValue = DateFilterOption.OnOrBefore;
-
- const wrapper = mountWithApp(
- ,
- );
-
- expect(wrapper).toContainReactComponent(DatePicker);
- });
-
- it('does not display DatePicker when filterValue is filter without date predicate', () => {
- const dateFilterValue = DateFilterOption.PastMonth;
-
- const wrapper = mountWithApp(
- ,
- );
-
- expect(wrapper).not.toContainReactComponent(DatePicker);
- });
-
- it('is used to calculate dateFilterOption and gets passed to Select as value', () => {
- const filterValue = 'filter value';
- const wrapper = mountWithApp(
- ,
- );
- expect(wrapper).toContainReactComponent(Select, {value: filterValue});
- });
- });
-
- describe('filterKey and filterMaxKey', () => {
- it('is used to calculate dateFilterOption and gets passed to Select as value', () => {
- const filterValue = 'filter value';
- const filterKey = 'before';
- const filterMaxKey = 'before';
- const wrapper = mountWithApp(
- ,
- );
- expect(wrapper).toContainReactComponent(Select, {value: 'on_or_before'});
- });
- });
-
- describe('timezones adjustments', () => {
- it('sets the selected date with negative timezone offset on DatePicker and TextField', () => {
- const nextUserInputDate = '2019-01-01';
- const timezoneOffset = -540;
- const timezoneOffsetInHours = Math.abs(timezoneOffset / 60);
- getTimezoneOffset.mockImplementation(() => timezoneOffset);
-
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(TextField)!.trigger('onChange', nextUserInputDate);
- wrapper.find(TextField)!.trigger('onBlur');
-
- const selectedDate = wrapper.find(DatePicker)!.prop('selected') as Date;
- const selectedInputDate = wrapper.find(TextField)!.prop('value');
-
- expect(selectedDate.toISOString()).toBe(
- `2019-01-01T0${timezoneOffsetInHours}:00:00.000Z`,
- );
- expect(selectedInputDate).toBe(nextUserInputDate);
- });
-
- it('sets the selected date with fringe timezone offset on DatePicker and TextField', () => {
- const nextUserInputDate = '2019-01-01';
- getTimezoneOffset.mockImplementation(() => -720);
-
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(TextField)!.trigger('onChange', nextUserInputDate);
- wrapper.find(TextField)!.trigger('onBlur');
-
- const selectedDate = wrapper.find(DatePicker)!.prop('selected') as Date;
- const selectedInputDate = wrapper.find(TextField)!.prop('value');
-
- expect(selectedDate.toISOString()).toContain(nextUserInputDate);
- expect(selectedInputDate).toBe(nextUserInputDate);
- });
-
- it('sets the selected date with positive timezone offset on DatePicker and TextField', () => {
- const nextUserInputDate = '2019-01-01';
- const timezoneOffset = 300;
- const timezoneOffsetInHours = Math.abs(timezoneOffset / 60);
- getTimezoneOffset.mockImplementation(() => timezoneOffset);
-
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(TextField)!.trigger('onChange', nextUserInputDate);
- wrapper.find(TextField)!.trigger('onBlur');
-
- const selectedDate = wrapper.find(DatePicker)!.prop('selected') as Date;
- const selectedInputDate = wrapper.find(TextField)!.prop('value');
-
- expect(selectedDate.toISOString()).toBe(
- `2019-01-01T0${timezoneOffsetInHours}:00:00.000Z`,
- );
- expect(selectedInputDate).toBe(nextUserInputDate);
- });
- });
-
- describe('filterKey and filterMinKey', () => {
- it('is used to calculate dateFilterOption and gets passed to Select as value', () => {
- const filterValue = 'filter value';
- const filterKey = 'after';
- const filterMinKey = 'after';
- const wrapper = mountWithApp(
- ,
- );
- expect(wrapper).toContainReactComponent(Select, {value: 'on_or_after'});
- });
- });
-
- describe('onFilterValueChange', () => {
- it('gets called with new filter value when date filter is updated to filter without date predicate', () => {
- const onFilterValueChangeSpy = jest.fn();
- const newDateFilter = DateFilterOption.PastMonth;
-
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(Select)!.trigger('onChange', newDateFilter);
-
- expect(onFilterValueChangeSpy).toHaveBeenCalledWith(newDateFilter);
- });
-
- it('gets called with undefined when date filter is updated to filter with minimum date predicate (on or after) and no current date selection', () => {
- const onFilterValueChangeSpy = jest.fn();
- const newDateFilter = DateFilterOption.OnOrAfter;
-
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(Select)!.trigger('onChange', newDateFilter);
-
- expect(onFilterValueChangeSpy).toHaveBeenCalledWith(undefined);
- });
-
- it('gets called with undefined when date filter is updated to filter with maximum date predicate (on or before) and no current date selection', () => {
- const onFilterValueChangeSpy = jest.fn();
- const newDateFilter = DateFilterOption.OnOrBefore;
-
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(Select)!.trigger('onChange', newDateFilter);
-
- expect(onFilterValueChangeSpy).toHaveBeenCalledWith(undefined);
- });
-
- it('gets called with formatted YYYY-MM-DD date when date filter is updated to filter with minimum date predicate (on or after) and current date selection', () => {
- const onFilterValueChangeSpy = jest.fn();
- const newDateFilter = DateFilterOption.OnOrAfter;
- const date = '2019-05-28';
-
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(DatePicker)!.trigger('onChange', {end: new Date(date)});
- wrapper.find(Select)!.trigger('onChange', newDateFilter);
-
- expect(onFilterValueChangeSpy).toHaveBeenCalledWith('2019-05-28');
- });
-
- it('gets called with formatted YYYY-MM-DD date when date filter is updated to filter with maximum date predicate (on or before) and current date selection', () => {
- const onFilterValueChangeSpy = jest.fn();
- const newDateFilter = DateFilterOption.OnOrBefore;
- const date = '2019-05-28';
-
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(DatePicker)!.trigger('onChange', {end: new Date(date)});
- wrapper.find(Select)!.trigger('onChange', newDateFilter);
-
- expect(onFilterValueChangeSpy).toHaveBeenCalledWith('2019-05-28');
- });
-
- it('gets called with formatted YYYY-MM-DD date when date is updated in DatePicker', () => {
- const onFilterValueChangeSpy = jest.fn();
- const dateFilter = DateFilterOption.OnOrBefore;
- const date = '2019-05-28';
-
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(DatePicker)!.trigger('onChange', {end: new Date(date)});
-
- expect(onFilterValueChangeSpy).toHaveBeenCalledWith('2019-05-28');
- });
-
- it('gets called with date when date is updated in TextField with YYYY-MM-DD date and TextField is blurred', () => {
- const onFilterValueChangeSpy = jest.fn();
- const dateFilter = DateFilterOption.OnOrBefore;
- const date = '2019-08-22';
-
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(TextField)!.trigger('onChange', date);
- wrapper.find(TextField)!.trigger('onBlur');
-
- expect(onFilterValueChangeSpy).toHaveBeenCalledWith(date);
- });
-
- it('gets called with undefined when date is updated in TextField with invalid date and TextField is blurred', () => {
- const onFilterValueChangeSpy = jest.fn();
- const dateFilter = DateFilterOption.OnOrBefore;
- const invalidDate = '2019/08/22';
-
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(TextField)!.trigger('onChange', invalidDate);
- wrapper.find(TextField)!.trigger('onBlur');
-
- expect(onFilterValueChangeSpy).toHaveBeenCalledWith(undefined);
- });
- });
-
- describe('onFilterKeyChange', () => {
- it('gets called with consumer filter key when date filter is updated to filter without date predicate', () => {
- const onFilterKeyChangeSpy = jest.fn();
- const initialConsumerFilterKey = 'starts';
-
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(Select)!.trigger('onChange', DateFilterOption.PastMonth);
-
- expect(onFilterKeyChangeSpy).toHaveBeenCalledWith(
- initialConsumerFilterKey,
- );
- });
-
- it('gets called with max filter key prop when date filter is updated to filter with maximum date predicate (on or before)', () => {
- const onFilterKeyChangeSpy = jest.fn();
- const filterMaxKey = 'starts_max';
-
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(Select)!.trigger('onChange', DateFilterOption.OnOrBefore);
-
- expect(onFilterKeyChangeSpy).toHaveBeenCalledWith(filterMaxKey);
- });
-
- it('gets called with min filter key when date filter is updated to filter with minimum date predicate (on or after)', () => {
- const onFilterKeyChangeSpy = jest.fn();
- const filterMinKey = 'starts_min';
-
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(Select)!.trigger('onChange', DateFilterOption.OnOrAfter);
-
- expect(onFilterKeyChangeSpy).toHaveBeenCalledWith(filterMinKey);
- });
- });
-
- it('resets date in DatePicker when user removes date in TextField', () => {
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(TextField)!.trigger('onChange', '');
- expect(wrapper).toContainReactComponent(DatePicker, {selected: undefined});
- });
-
- it('updates selected date in DatePicker when user enters a valid date in TextField and field is blurred', () => {
- const validUserInputDate = '2020-08-30';
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(TextField)!.trigger('onChange', validUserInputDate);
- wrapper.find(TextField)!.trigger('onBlur');
-
- const selectedDate = wrapper.find(DatePicker)!.prop('selected') as Date;
- expect(selectedDate.toISOString()).toContain(validUserInputDate);
- });
-
- it('does not update selected date in DatePicker when user enters an invalid date in TextField and field is blurred', () => {
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(TextField)!.trigger('onChange', 'INVALID');
- wrapper.find(TextField)!.trigger('onBlur');
-
- expect(wrapper).toContainReactComponent(DatePicker, {selected: undefined});
- });
-
- it('resets selected date in DatePicker when user enters an invalid date in TextField and field is blurred', () => {
- const invalidUserInputDate = '08/20/2020';
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(TextField)!.trigger('onChange', invalidUserInputDate);
- wrapper.find(TextField)!.trigger('onBlur');
-
- expect(wrapper).toContainReactComponent(DatePicker, {selected: undefined});
- });
-
- it('removes date field error when invalid date is replaced by valid date in TextField', () => {
- const validUserInputDate = '2020-08-30';
- const invalidUserInputDate = '08/30/2020';
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(TextField)!.trigger('onChange', invalidUserInputDate);
- wrapper.find(TextField)!.trigger('onBlur');
-
- wrapper.find(TextField)!.trigger('onChange', validUserInputDate);
-
- expect(wrapper).toContainReactComponent(TextField, {error: undefined});
- });
-
- it('removes date field error when new date is selected in DatePicker', () => {
- const invalidUserInputDate = '08/30/2020';
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(TextField)!.trigger('onChange', invalidUserInputDate);
- wrapper.find(TextField)!.trigger('onBlur');
-
- wrapper.find(DatePicker)!.trigger('onChange', {end: new Date()});
-
- expect(wrapper).toContainReactComponent(TextField, {error: undefined});
- });
-
- it('does not display error when date is added in date filed by DatePicker and date field is blurred', () => {
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(DatePicker)!.trigger('onChange', {end: new Date()});
- wrapper.find(TextField)!.trigger('onBlur');
-
- expect(wrapper.find(TextField)!.prop('error')).toBeUndefined();
- });
-
- function getOptionsValuesList(options?: any) {
- if (!options) {
- return [];
- }
-
- return options.map((option: any) => {
- return typeof option === 'string' ? option : option.value;
- });
- }
-});
-
-function noop() {}
diff --git a/src/components/ResourceList/components/FilterControl/components/FilterCreator/FilterCreator.tsx b/src/components/ResourceList/components/FilterControl/components/FilterCreator/FilterCreator.tsx
deleted file mode 100644
index 6b17dcae097..00000000000
--- a/src/components/ResourceList/components/FilterControl/components/FilterCreator/FilterCreator.tsx
+++ /dev/null
@@ -1,179 +0,0 @@
-import React, {useRef, useState, useCallback} from 'react';
-
-import {Button} from '../../../../../Button';
-import {Popover} from '../../../../../Popover';
-import {Select} from '../../../../../Select';
-import {FormLayout} from '../../../../../FormLayout';
-import {Form} from '../../../../../Form';
-import {useToggle} from '../../../../../../utilities/use-toggle';
-import {useI18n} from '../../../../../../utilities/i18n';
-import {FilterValueSelector} from '../FilterValueSelector';
-import type {AppliedFilter, Filter, Operator} from '../../types';
-
-export interface FilterCreatorProps {
- filters: Filter[];
- resourceName: {
- singular: string;
- plural: string;
- };
- disabled?: boolean;
- onAddFilter?(newFilter: AppliedFilter): void;
-}
-
-export function FilterCreator({
- filters,
- resourceName,
- disabled,
- onAddFilter,
-}: FilterCreatorProps) {
- const {
- value: popoverActive,
- toggle: togglePopoverActive,
- setFalse: setPopoverActiveFalse,
- } = useToggle(false);
- const [selectedFilter, setSelectedFilter] = useState();
- const [selectedFilterKey, setSelectedFilterKey] =
- useState();
- const [selectedFilterValue, setSelectedFilterValue] =
- useState();
- const i18n = useI18n();
- const node = useRef(null);
-
- const canAddFilter = Boolean(
- selectedFilter && selectedFilterKey && selectedFilterValue,
- );
-
- const handleButtonFocus = useCallback(
- (...args: React.FocusEvent[]) => {
- const event = args[0];
- if (!node.current && event) {
- node.current = event.target;
- }
- },
- [],
- );
- const handleFilterKeyChange = useCallback(
- (filterKey: string) => {
- const foundFilter = filters.find((filter: any) => {
- const {minKey, maxKey, operatorText} = filter;
-
- if (minKey || maxKey) {
- return (
- filter.key === filterKey ||
- minKey === filterKey ||
- maxKey === filterKey
- );
- }
-
- if (operatorText && typeof operatorText !== 'string') {
- return (
- filter.key === filterKey ||
- operatorText.filter(({key}: Operator) => key === filterKey)
- .length === 1
- );
- }
-
- return filter.key === filterKey;
- });
-
- if (!foundFilter) {
- return;
- }
-
- setSelectedFilter(foundFilter);
- setSelectedFilterKey(filterKey);
- setSelectedFilterValue(undefined);
- },
- [filters],
- );
- const handleFilterValueChange = useCallback((value: string) => {
- setSelectedFilterValue(value);
- }, []);
- const handleAddFilter = useCallback(() => {
- if (!onAddFilter || !canAddFilter || !selectedFilterKey) {
- return;
- }
-
- onAddFilter({
- key: selectedFilterKey,
- value: selectedFilterValue || '',
- });
-
- setPopoverActiveFalse();
- setSelectedFilter(undefined);
- setSelectedFilterValue(undefined);
-
- if (node.current != null) {
- node.current.focus();
- }
- }, [
- canAddFilter,
- onAddFilter,
- selectedFilterKey,
- selectedFilterValue,
- setPopoverActiveFalse,
- ]);
-
- const activator = (
-
- );
-
- const filterOptions = filters.map(({key, label}) => ({
- value: key,
- label,
- }));
-
- const filterValueSelectionMarkup = selectedFilter ? (
-
- ) : null;
-
- const addFilterButtonMarkup = selectedFilter ? (
-
- ) : null;
-
- return (
-
-
-
- );
-}
diff --git a/src/components/ResourceList/components/FilterControl/components/FilterCreator/index.ts b/src/components/ResourceList/components/FilterControl/components/FilterCreator/index.ts
deleted file mode 100644
index b69649992e3..00000000000
--- a/src/components/ResourceList/components/FilterControl/components/FilterCreator/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './FilterCreator';
diff --git a/src/components/ResourceList/components/FilterControl/components/FilterCreator/tests/FilterCreator.test.tsx b/src/components/ResourceList/components/FilterControl/components/FilterCreator/tests/FilterCreator.test.tsx
deleted file mode 100644
index 0e32a617c6d..00000000000
--- a/src/components/ResourceList/components/FilterControl/components/FilterCreator/tests/FilterCreator.test.tsx
+++ /dev/null
@@ -1,329 +0,0 @@
-import React from 'react';
-import {mountWithApp, CustomRoot} from 'tests/utilities';
-
-import {Button} from '../../../../../../Button';
-import {Form} from '../../../../../../Form';
-import {Popover} from '../../../../../../Popover';
-import {Select} from '../../../../../../Select';
-import {FilterCreator, FilterCreatorProps} from '../FilterCreator';
-import {FilterValueSelector} from '../../FilterValueSelector';
-import {FilterType} from '../../../types';
-
-jest.mock('../../../../../../Popover/components', () => ({
- PopoverOverlay: function PopoverOverlay({children}: any) {
- return children;
- },
-}));
-
-describe('', () => {
- const mockDefaultProps: FilterCreatorProps = {
- filters: [
- {
- key: 'filterKey',
- label: 'Product type',
- operatorText: 'is',
- type: FilterType.Select,
- options: [
- 'Bundle',
- {
- value: 'electronic_value',
- label: 'Electronic',
- disabled: true,
- },
- {
- value: 'beauty_value',
- label: 'Beauty',
- },
- ],
- },
- {
- key: 'filterKey2',
- label: 'Tagged with',
- type: FilterType.TextField,
- },
- {
- key: 'filterKey3',
- label: 'Times used',
- operatorText: [
- {
- optionLabel: 'less than',
- key: 'times_used_max',
- },
- {
- optionLabel: 'greater than',
- key: 'times_used_min',
- },
- ],
- type: FilterType.TextField,
- },
- ],
- resourceName: {
- singular: 'Item',
- plural: 'Items',
- },
- disabled: false,
- };
-
- it('focuses the activator after adding a filter', () => {
- const filterCreator = mountWithApp(
- {}} />,
- );
-
- filterCreator
- .find(Select)!
- .trigger('onChange', mockDefaultProps.filters[0].key);
-
- const activator = filterCreator.find(Button, {children: 'Filter'})!;
- const activatorButton = activator.find('button')!;
-
- // This any cast is needed as the activator's onFocus actually uses the
- // event argument that gets passed into the onFocus handler, though we type
- // onFocus as accepting no arguments
- (activator as any).trigger('onFocus', {
- target: activatorButton.domNode,
- });
- filterCreator.find(FilterValueSelector)!.trigger('onChange', 'x');
-
- filterCreator.find(Button, {children: 'Add filter'})!.trigger('onClick');
-
- expect(document.activeElement).toBe(activatorButton.domNode);
- });
-
- it('does not focus the activator after adding a filter if focus was never originally received by the by activator', () => {
- const filterCreator = mountWithApp(
- {}} />,
- );
-
- filterCreator
- .find(Select)!
- .trigger('onChange', mockDefaultProps.filters[0].key);
-
- const activator = filterCreator.find(Button, {children: 'Filter'})!;
-
- // This any cast is needed as the activator's onFocus actually uses the
- // event argument that gets passed into the onFocus handler, though we type
- // onFocus as accepting no arguments
- (activator as any).trigger('onFocus', {target: activator!.domNode});
-
- filterCreator.find(Button, {children: 'Add filter'})!.trigger('onClick');
-
- expect(document.activeElement).not.toBe(activator.domNode);
- });
-
- it('renders just a button by default', () => {
- const wrapper = mountWithApp();
-
- expect(wrapper).toContainReactComponentTimes(Button, 1);
- expect(wrapper).toContainReactComponent(Button, {disclosure: true});
- // expect(wrapper.find(Button)).toHaveLength(1);
- });
-
- it('renders a non-active popover on default', () => {
- const wrapper = mountWithApp();
-
- expect(wrapper).toContainReactComponent(Popover, {active: false});
- });
-
- it('renders a active popover with a Select on click of the activator button', () => {
- const wrapper = mountWithApp();
-
- activatePopover(wrapper);
-
- expect(wrapper).toContainReactComponent(Popover, {active: true});
- // expect(findFilterKeySelect(wrapper).exists()).toBe(true);
-
- expect(wrapper.find(Popover)).toContainReactComponent(Select);
- });
-
- it('renders a non-active popover after add filter button was clicked and onAddFilter was triggered', () => {
- const onAddFilter = jest.fn();
- const wrapper = mountWithApp(
- ,
- );
-
- activatePopover(wrapper);
- selectFilterKey(wrapper, mockDefaultProps.filters[0].key);
- selectFilterValue(wrapper, 'Bundle');
- clickAddFilter(wrapper);
-
- expect(onAddFilter).toHaveBeenCalled();
- expect(wrapper).toContainReactComponent(Popover, {active: false});
- });
-
- it('does not render FilterValueSelector after add filter button was clicked', () => {
- const onAddFilter = jest.fn();
- const wrapper = mountWithApp(
- ,
- );
-
- activatePopover(wrapper);
- selectFilterKey(wrapper, mockDefaultProps.filters[0].key);
- selectFilterValue(wrapper, 'Bundle');
-
- expect(wrapper).toContainReactComponent(FilterValueSelector);
- clickAddFilter(wrapper);
- expect(wrapper).not.toContainReactComponent(FilterValueSelector);
- });
-
- it('renders Select with no value after add filter button was clicked', () => {
- const onAddFilter = jest.fn();
- const wrapper = mountWithApp(
- ,
- );
-
- activatePopover(wrapper);
- selectFilterKey(wrapper, mockDefaultProps.filters[0].key);
- selectFilterValue(wrapper, 'Bundle');
- const select = wrapper.findAll(Select)[0];
- expect(select.prop('value')).toBeDefined();
- clickAddFilter(wrapper);
- expect(wrapper).toContainReactComponent(Select, {value: undefined});
- });
-
- describe('filters', () => {
- it('sets the options when popover is active', () => {
- const wrapper = mountWithApp();
-
- activatePopover(wrapper);
- expect(wrapper.find(Popover)).toContainReactComponent(Select, {
- options: [
- {
- value: mockDefaultProps.filters[0].key,
- label: mockDefaultProps.filters[0].label,
- },
- {
- value: mockDefaultProps.filters[1].key,
- label: mockDefaultProps.filters[1].label,
- },
- {
- value: mockDefaultProps.filters[2].key,
- label: mockDefaultProps.filters[2].label,
- },
- ],
- });
- });
- });
-
- describe('', () => {
- it('does not render by default', () => {
- const wrapper = mountWithApp();
-
- activatePopover(wrapper);
-
- expect(wrapper).not.toContainReactComponent(FilterValueSelector);
- });
-
- it('updates FilterValueSelector when user selects a filter key', () => {
- const wrapper = mountWithApp();
-
- activatePopover(wrapper);
- selectFilterKey(wrapper, mockDefaultProps.filters[1].key);
-
- expect(wrapper).toContainReactComponent(FilterValueSelector, {
- filter: mockDefaultProps.filters[1],
- value: undefined,
- });
- });
-
- it('updates value with provided string when user selects a filter value', () => {
- const wrapper = mountWithApp();
-
- activatePopover(wrapper);
- selectFilterKey(wrapper, mockDefaultProps.filters[0].key);
- selectFilterValue(wrapper, 'Bundle');
-
- expect(wrapper).toContainReactComponent(FilterValueSelector, {
- value: 'Bundle',
- });
- });
-
- it('updates FilterValueSelector when filter key is updated to existing operator key', () => {
- const wrapper = mountWithApp();
-
- const newOperatorKey = 'times_used_max';
-
- activatePopover(wrapper);
- selectFilterKey(wrapper, mockDefaultProps.filters[2].key);
- selectFilterValue(wrapper, 'Bundle');
-
- wrapper
- .find(FilterValueSelector)!
- .trigger('onFilterKeyChange', newOperatorKey);
-
- expect(wrapper).toContainReactComponent(FilterValueSelector, {
- filterKey: newOperatorKey,
- });
- });
- });
-
- describe('filter add button', () => {
- it('is enabled when filter key and filter value are both selected', () => {
- const wrapper = mountWithApp();
-
- activatePopover(wrapper);
- selectFilterKey(wrapper, mockDefaultProps.filters[0].key);
- selectFilterValue(wrapper, 'Bundle');
-
- expect(wrapper.find(Form)).toContainReactComponent(Button, {
- disabled: false,
- });
- });
-
- it('is disabled when filter key and value are not selected', () => {
- const wrapper = mountWithApp();
-
- activatePopover(wrapper);
- selectFilterKey(wrapper, mockDefaultProps.filters[0].key);
-
- expect(wrapper.find(Form)).toContainReactComponent(Button, {
- disabled: true,
- });
- });
-
- it('is disabled when filter value is an empty string', () => {
- const wrapper = mountWithApp();
-
- activatePopover(wrapper);
- selectFilterKey(wrapper, mockDefaultProps.filters[0].key);
- selectFilterValue(wrapper, '');
-
- expect(wrapper.find(Form)).toContainReactComponent(Button, {
- disabled: true,
- });
- });
- });
-
- describe('onAddFilter', () => {
- it('gets call with selected filter key & value when both value are valid and add filter button was clicked', () => {
- const onAddFilter = jest.fn();
- const wrapper = mountWithApp(
- ,
- );
- activatePopover(wrapper);
- selectFilterKey(wrapper, mockDefaultProps.filters[0].key);
- selectFilterValue(wrapper, 'Bundle');
- clickAddFilter(wrapper);
-
- expect(onAddFilter).toHaveBeenCalledWith({
- key: mockDefaultProps.filters[0].key,
- value: 'Bundle',
- });
- });
- });
-});
-
-function activatePopover(wrapper: CustomRoot) {
- wrapper.find(Button, {disclosure: true})!.trigger('onClick');
-}
-
-function selectFilterKey(wrapper: CustomRoot, filterKey: string) {
- wrapper.find(Select)!.trigger('onChange', filterKey);
-}
-
-function selectFilterValue(wrapper: CustomRoot, filterValue: string) {
- wrapper.find(FilterValueSelector)!.trigger('onChange', filterValue);
-}
-
-function clickAddFilter(wrapper: CustomRoot) {
- wrapper.find(Form)?.find(Button)!.trigger('onClick');
-}
diff --git a/src/components/ResourceList/components/FilterControl/components/FilterValueSelector/FilterValueSelector.tsx b/src/components/ResourceList/components/FilterControl/components/FilterValueSelector/FilterValueSelector.tsx
deleted file mode 100644
index e07e467b5d8..00000000000
--- a/src/components/ResourceList/components/FilterControl/components/FilterValueSelector/FilterValueSelector.tsx
+++ /dev/null
@@ -1,118 +0,0 @@
-import React, {useCallback} from 'react';
-
-import {Select} from '../../../../../Select';
-import {Stack} from '../../../../../Stack';
-import {TextField} from '../../../../../TextField';
-import {DateSelector} from '../DateSelector';
-import {Filter, AppliedFilter, FilterType, Operator} from '../../types';
-import {useI18n} from '../../../../../../utilities/i18n';
-import {useIsMountedRef} from '../../../../../../utilities/use-is-mounted-ref';
-
-export interface FilterValueSelectorProps {
- filter: Filter;
- filterKey?: string;
- value?: AppliedFilter['value'];
- onChange(filterValue: AppliedFilter['value']): void;
- onFilterKeyChange(filterKey: string): void;
-}
-
-export function FilterValueSelector({
- filter,
- filterKey,
- value,
- onChange,
- onFilterKeyChange,
-}: FilterValueSelectorProps) {
- const i18n = useI18n();
- const isMounted = useIsMountedRef();
- const {operatorText, type, label} = filter;
- const showOperatorOptions =
- type !== FilterType.DateSelector &&
- operatorText &&
- typeof operatorText !== 'string';
-
- const handleOperatorOptionChange = useCallback(
- (operatorKey: string) => {
- onFilterKeyChange(operatorKey);
-
- if (!value) {
- return;
- }
-
- onChange(value);
- },
- [onChange, onFilterKeyChange, value],
- );
-
- if (showOperatorOptions && operatorText!.length !== 0 && !isMounted.current) {
- handleOperatorOptionChange((operatorText as Operator[])[0].key);
- }
-
- const operatorOptionsMarkup = showOperatorOptions ? (
-
- ) : null;
-
- const selectedFilterLabel =
- typeof operatorText === 'string' ? operatorText : '';
-
- switch (filter.type) {
- case FilterType.Select:
- return (
-
- {operatorOptionsMarkup}
-
-
- );
- case FilterType.TextField:
- return (
-
- {operatorOptionsMarkup}
-
-
- );
- case FilterType.DateSelector:
- return (
-
- );
- default:
- return null;
- }
-}
-
-function buildOperatorOptions(operatorText?: string | Operator[]) {
- if (!operatorText || typeof operatorText === 'string') {
- return [];
- }
-
- return operatorText.map(({key, optionLabel}) => {
- return {value: key, label: optionLabel};
- });
-}
diff --git a/src/components/ResourceList/components/FilterControl/components/FilterValueSelector/index.ts b/src/components/ResourceList/components/FilterControl/components/FilterValueSelector/index.ts
deleted file mode 100644
index 8bfbb83c12b..00000000000
--- a/src/components/ResourceList/components/FilterControl/components/FilterValueSelector/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './FilterValueSelector';
diff --git a/src/components/ResourceList/components/FilterControl/components/FilterValueSelector/tests/FilterValueSelector.test.tsx b/src/components/ResourceList/components/FilterControl/components/FilterValueSelector/tests/FilterValueSelector.test.tsx
deleted file mode 100644
index e06b677c4ae..00000000000
--- a/src/components/ResourceList/components/FilterControl/components/FilterValueSelector/tests/FilterValueSelector.test.tsx
+++ /dev/null
@@ -1,442 +0,0 @@
-import React from 'react';
-import {mountWithApp} from 'tests/utilities';
-
-import {Select} from '../../../../../../Select';
-import {TextField} from '../../../../../../TextField';
-import {FilterValueSelector} from '../FilterValueSelector';
-import {DateSelector} from '../../DateSelector';
-import {Filter, FilterType, Operator} from '../../../types';
-
-describe('', () => {
- const operators = [
- {
- optionLabel: 'equal to',
- filterLabel: 'is',
- key: 'times_used',
- },
- {
- optionLabel: 'not equal to',
- filterLabel: 'is not',
- key: 'times_used_not',
- },
- {
- optionLabel: 'less than',
- filterLabel: 'is less than',
- key: 'times_used_max',
- },
- {
- optionLabel: 'greater than',
- filterLabel: 'is greater than',
- key: 'times_used_min',
- },
- ];
-
- describe('filter.type', () => {
- describe('FilterType.Select', () => {
- const filter: Filter = {
- key: 'filterKey1',
- label: 'Product type',
- operatorText: 'is',
- type: FilterType.Select,
- options: [
- 'Bundle',
- {
- value: 'electronic_value',
- label: 'Electronic',
- disabled: true,
- },
- {
- value: 'beauty_value',
- label: 'Beauty',
- },
- ],
- };
-
- it('renders a Select field', () => {
- const wrapper = mountWithApp(
- ,
- );
-
- expect(wrapper).toContainReactComponent(Select);
- });
-
- it('renders label using operatorText when it is a string', () => {
- const wrapper = mountWithApp(
- ,
- );
-
- expect(wrapper).toContainReactComponent(Select, {
- label: filter.operatorText,
- });
- });
-
- it('renders a Select with options using operatorText when it is a list of operators', () => {
- const wrapper = mountWithApp(
- ,
- );
-
- const expectedOptions = getOptionsListForOperators(operators);
-
- expect(wrapper).toContainReactComponent(Select, {
- options: expectedOptions,
- });
- });
-
- it('renders value using the value prop', () => {
- const value = 'test';
- const wrapper = mountWithApp(
- ,
- );
-
- // const select = wrapper.find(Select);
- // expect(select.prop('value')).toBe(value);
- expect(wrapper).toContainReactComponent(Select, {value});
- });
-
- it('calls onChange when the Select was changed', () => {
- const onChange = jest.fn();
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(Select)?.trigger('onChange');
- expect(onChange).toHaveBeenCalled();
- });
-
- it('calls onFilterKeyChange when operator is changed', () => {
- const onFilterKeyChangeSpy = jest.fn();
- const newOperator = operators[1].key;
-
- const wrapper = mountWithApp(
- ,
- );
-
- const operatorsSelect = wrapper.findAll(Select)[0];
- operatorsSelect.trigger('onChange', newOperator);
-
- expect(onFilterKeyChangeSpy).toHaveBeenCalledWith(newOperator);
- });
-
- it('calls onChange with filter value when operator is changed and filter value is set', () => {
- const onChangeSpy = jest.fn();
- const newFilterValue = 'foo';
-
- const wrapper = mountWithApp(
- ,
- );
-
- const selects = wrapper.findAll(Select);
-
- const filterValueSelect = selects[1];
- filterValueSelect.trigger('onChange', newFilterValue);
-
- const operatorsSelect = selects[0];
- operatorsSelect.trigger('onChange', operators[1].key);
-
- expect(onChangeSpy).toHaveBeenCalledWith(newFilterValue);
- });
- });
-
- describe('FilterType.TextField', () => {
- const filter: Filter = {
- key: 'filterKey2',
- label: 'Tagged with',
- type: FilterType.TextField,
- };
-
- it('renders a TextField', () => {
- const wrapper = mountWithApp(
- ,
- );
-
- expect(wrapper).toContainReactComponent(TextField);
- });
-
- it('renders label as empty string if operatorText does not exist', () => {
- const wrapper = mountWithApp(
- ,
- );
-
- expect(wrapper).toContainReactComponent(TextField, {label: ''});
- });
-
- it('renders a Select with options using operatorText when it is a list of operators', () => {
- const wrapper = mountWithApp(
- ,
- );
-
- const expectedOptions = getOptionsListForOperators(operators);
-
- expect(wrapper).toContainReactComponent(Select, {
- options: expectedOptions,
- });
- });
-
- it('renders value using the value prop', () => {
- const value = 'test';
- const wrapper = mountWithApp(
- ,
- );
-
- expect(wrapper).toContainReactComponent(TextField, {value});
- });
-
- it('renders type using the textFieldType prop', () => {
- const textFieldType = 'number';
-
- const wrapper = mountWithApp(
- ,
- );
-
- expect(wrapper).toContainReactComponent(TextField, {
- type: textFieldType,
- });
- });
-
- it('renders undefined type when the textFieldType prop is not passed', () => {
- const wrapper = mountWithApp(
- ,
- );
-
- expect(wrapper).toContainReactComponent(TextField, {
- type: undefined,
- });
- });
-
- it('calls onChange when the text field was changed', () => {
- const onChange = jest.fn();
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(TextField)!.trigger('onChange');
- expect(onChange).toHaveBeenCalled();
- });
-
- it('calls onFilterKeyChange when operator is changed', () => {
- const onFilterKeyChangeSpy = jest.fn();
- const newOperator = operators[1].key;
-
- const wrapper = mountWithApp(
- ,
- );
-
- const operatorsSelect = wrapper.findAll(Select)[0];
- operatorsSelect.trigger('onChange', newOperator);
-
- expect(onFilterKeyChangeSpy).toHaveBeenCalledWith(newOperator);
- });
-
- it('calls onChange with filter value when operator is changed and filter value is set', () => {
- const onChangeSpy = jest.fn();
- const newFilterValue = 'foo';
-
- const wrapper = mountWithApp(
- ,
- );
-
- const filterValueTextField = wrapper.find(TextField)!;
- filterValueTextField.trigger('onChange', newFilterValue);
-
- const operatorsSelect = wrapper.findAll(Select)[0];
- operatorsSelect.trigger('onChange', operators[1].key);
-
- expect(onChangeSpy).toHaveBeenCalledWith(newFilterValue);
- });
- });
-
- describe('FilterType.DateSelector', () => {
- const filter: Filter = {
- key: 'filterKey1',
- minKey: 'ends_min',
- maxKey: 'ends_max',
- label: 'Starts',
- type: FilterType.DateSelector,
- dateOptionType: 'past',
- };
-
- it('renders a DateSelector', () => {
- const wrapper = mountWithApp(
- ,
- );
-
- expect(wrapper).toContainReactComponent(DateSelector);
- });
-
- it('renders filterValue using the value prop', () => {
- const filterValue = 'test';
- const wrapper = mountWithApp(
- ,
- );
-
- expect(wrapper).toContainReactComponent(DateSelector, {
- filterValue,
- });
- });
-
- it('renders filterKey using the filterKey prop', () => {
- const filterKey = 'test';
- const wrapper = mountWithApp(
- ,
- );
-
- expect(wrapper).toContainReactComponent(DateSelector, {
- filterKey,
- });
- });
-
- it('renders filterMinKey using the min key of filter on filter prop', () => {
- const filterMinKey = 'test';
- const mockFilter = {...filter, minKey: filterMinKey};
-
- const wrapper = mountWithApp(
- ,
- );
-
- expect(wrapper).toContainReactComponent(DateSelector, {
- filterMinKey,
- });
- });
-
- it('renders filterMaxKey using the max key of filter on filter prop', () => {
- const filterMaxKey = 'test';
- const mockFilter = {...filter, maxKey: filterMaxKey};
-
- const wrapper = mountWithApp(
- ,
- );
-
- expect(wrapper).toContainReactComponent(DateSelector, {
- filterMaxKey,
- });
- });
-
- it('calls onChange when the filter key was changed', () => {
- const onChange = jest.fn();
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(DateSelector)!.trigger('onFilterValueChange');
- expect(onChange).toHaveBeenCalled();
- });
-
- it('calls onFilterKeyChange when the filter key was changed', () => {
- const onFilterKeyChange = jest.fn();
- const wrapper = mountWithApp(
- ,
- );
-
- wrapper.find(DateSelector)!.trigger('onFilterKeyChange');
- expect(onFilterKeyChange).toHaveBeenCalled();
- });
- });
- });
-
- function getOptionsListForOperators(operators: Operator[]) {
- return operators.map(({key, optionLabel}) => {
- return {value: key, label: optionLabel};
- });
- }
-});
-
-function noop() {}
diff --git a/src/components/ResourceList/components/FilterControl/components/index.ts b/src/components/ResourceList/components/FilterControl/components/index.ts
deleted file mode 100644
index 6524ac21db2..00000000000
--- a/src/components/ResourceList/components/FilterControl/components/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export * from './DateSelector';
-
-export * from './FilterCreator';
-
-export * from './FilterValueSelector';
diff --git a/src/components/ResourceList/components/FilterControl/index.ts b/src/components/ResourceList/components/FilterControl/index.ts
deleted file mode 100644
index 94187017fae..00000000000
--- a/src/components/ResourceList/components/FilterControl/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './FilterControl';
diff --git a/src/components/ResourceList/components/FilterControl/tests/FilterControl.test.tsx b/src/components/ResourceList/components/FilterControl/tests/FilterControl.test.tsx
deleted file mode 100644
index cf9a3c296a4..00000000000
--- a/src/components/ResourceList/components/FilterControl/tests/FilterControl.test.tsx
+++ /dev/null
@@ -1,760 +0,0 @@
-import React from 'react';
-import {mountWithApp} from 'tests/utilities';
-
-import {Button} from '../../../../Button';
-import {TextField} from '../../../../TextField';
-import {Tag} from '../../../../Tag';
-import {ResourceListContext} from '../../../../../utilities/resource-list';
-import {
- Filter,
- FilterType,
- FilterSelect,
- FilterTextField,
- FilterDateSelector,
-} from '../types';
-// eslint-disable-next-line import/no-deprecated
-import {FilterControl, FilterControlProps} from '../FilterControl';
-import {FilterCreator, DateFilterOption} from '../components';
-
-describe('', () => {
- const mockDefaultProps: FilterControlProps = {
- onSearchChange: noop,
- };
-
- const mockDefaultContext = {
- selectMode: false,
- resourceName: {
- singular: 'item',
- plural: 'items',
- },
- selectable: false,
- };
-
- const mockFilters: Filter[] = [
- {
- key: 'filterKey1',
- label: 'Product type',
- operatorText: 'is',
- type: FilterType.Select,
- options: [
- 'Bundle',
- {
- value: 'electronic_value',
- label: 'Electronic',
- disabled: true,
- },
- {
- value: 'beauty_value',
- label: 'Beauty',
- },
- ],
- },
- {
- key: 'filterKey2',
- label: 'Tagged',
- operatorText: 'with',
- type: FilterType.TextField,
- },
- ];
-
- const mockAppliedFilters = [
- {
- key: 'filterKey1',
- value: 'Bundle',
- },
- {
- key: 'filterKey1',
- value: 'beauty_value',
- },
- ];
-
- describe('searchValue', () => {
- it('renders with TextField by default', () => {
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- expect(filterControl).toContainReactComponent(TextField);
- });
-
- it('renders with searchValue as its value', () => {
- const searchValue = 'search value';
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- expect(filterControl).toContainReactComponent(TextField, {
- value: searchValue,
- });
- });
- });
-
- describe('appliedFilters', () => {
- it('renders the same number of Tag as appliedFilters', () => {
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- const tags = filterControl.findAll(Tag);
- expect(tags).toHaveLength(mockAppliedFilters.length);
- });
-
- it('calls onFiltersChange without the applied filter when user clicks remove on the appliedFilter', () => {
- const onFiltersChange = jest.fn();
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- const tag = filterControl.findAll(Tag)[0];
- tag.trigger('onRemove');
-
- expect(onFiltersChange).toHaveBeenCalledWith([
- ...mockAppliedFilters.slice(1, mockAppliedFilters.length),
- ]);
- });
-
- it('renders the provided applied filter string when applied filter label exist', () => {
- const appliedFilterLabel = 'shorten electronic';
- const filter: FilterSelect = {
- key: 'filterKey1',
- label: 'Product type',
- operatorText: 'is',
- type: FilterType.Select,
- options: [
- {
- value: 'electronic_value',
- label: 'Electronic',
- },
- ],
- };
- const appliedFilters = {
- key: filter.key,
- value: appliedFilterLabel,
- };
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- const firstTag = filterControl.findAll(Tag)[0];
-
- expect(firstTag).toContainReactText(
- `${filter.label} ${filter.operatorText} ${appliedFilterLabel}`,
- );
- });
-
- it('renders the provided applied filter string when filter value exist in FilterSelect as an option string', () => {
- const filterValue = 'Bundle';
- const filter: FilterSelect = {
- key: 'filterKey1',
- label: 'Product type',
- operatorText: 'is',
- type: FilterType.Select,
- options: [filterValue],
- };
- const appliedFilters = {
- key: filter.key,
- value: filterValue,
- };
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- const firstTag = filterControl.findAll(Tag)[0];
-
- expect(firstTag).toContainReactText(
- `${filter.label} ${filter.operatorText} ${filterValue}`,
- );
- });
-
- it('renders the provided applied filter string when filter value exist in FilterSelect as an option object', () => {
- const filterValue = 'Electronic';
- const filter: FilterSelect = {
- key: 'filterKey1',
- label: 'Product type',
- operatorText: 'is',
- type: FilterType.Select,
- options: [
- {
- value: 'electronic_value',
- label: filterValue,
- },
- ],
- };
- const appliedFilters = {
- key: filter.key,
- value: filterValue,
- };
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- const firstTag = filterControl.findAll(Tag)[0];
- expect(firstTag).toContainReactText(
- `${filter.label} ${filter.operatorText} ${filterValue}`,
- );
- });
-
- it('renders the provided applied filter string when filter value cannot be found in FilterSelect options', () => {
- const appliedFilterValue = 'new Bundle';
- const filter: FilterSelect = {
- key: 'filterKey1',
- label: 'Product type',
- operatorText: 'is',
- type: FilterType.Select,
- options: ['Bundle'],
- };
- const appliedFilters = {
- key: filter.key,
- value: appliedFilterValue,
- };
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- const firstTag = filterControl.findAll(Tag)[0];
- expect(firstTag).toContainReactText(
- `${filter.label} ${filter.operatorText} ${appliedFilterValue}`,
- );
- });
-
- it('renders the provided applied filter string when filter is a FilterTextField', () => {
- const appliedFilterValue = 'new Bundle';
- const filter: FilterTextField = {
- key: 'filterKey2',
- label: 'Tagged',
- operatorText: 'with',
- type: FilterType.TextField,
- };
- const appliedFilters = {
- key: filter.key,
- value: appliedFilterValue,
- };
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- const firstTag = filterControl.findAll(Tag)[0];
- expect(firstTag).toContainReactText(
- `${filter.label} ${filter.operatorText} ${appliedFilterValue}`,
- );
- });
-
- it('renders the provided localized applied filter string when filter is a FilterDateSelector without date predicate', () => {
- const appliedFilterValue = DateFilterOption.PastWeek;
-
- const filter: FilterDateSelector = {
- key: 'starts',
- minKey: 'starts_min',
- maxKey: 'starts_max',
- label: 'Starts',
- operatorText: 'is',
- type: FilterType.DateSelector,
- };
- const appliedFilters = {
- key: filter.key,
- value: appliedFilterValue,
- };
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- const firstTag = filterControl.findAll(Tag)[0];
- expect(firstTag).toContainReactText(
- `${filter.label} ${filter.operatorText} in the last week`,
- );
- });
-
- it('renders the provided localized applied filter string when filter is a FilterDateSelector with minimum date predicate (on or after)', () => {
- const selectedDate = '2018-09-16';
- const filter: FilterDateSelector = {
- key: 'starts',
- minKey: 'starts_min',
- maxKey: 'starts_max',
- label: 'Starts',
- operatorText: 'is',
- type: FilterType.DateSelector,
- };
- const appliedFilters = {
- key: filter.minKey,
- value: selectedDate,
- };
-
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- const firstTag = filterControl.findAll(Tag)[0];
- expect(firstTag).toContainReactText(`${filter.label} after 9/16/2018`);
- });
-
- it('renders the provided localized applied filter string when filter is a FilterDateSelector with maximum date predicate (on or before)', () => {
- const selectedDate = '2018-09-16';
- const filter: FilterDateSelector = {
- key: 'starts',
- minKey: 'starts_min',
- maxKey: 'starts_max',
- label: 'Starts',
- operatorText: 'is',
- type: FilterType.DateSelector,
- };
- const appliedFilters = {
- key: filter.maxKey,
- value: selectedDate,
- };
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- const firstTag = filterControl.findAll(Tag)[0];
- expect(firstTag).toContainReactText(`${filter.label} before 9/16/2018`);
- });
-
- it('renders applied filter value when filter is a FilterDateSelector with invalid date predicate', () => {
- const selectedDate = 'INVALID';
- const filter: FilterDateSelector = {
- key: 'starts',
- minKey: 'starts_min',
- maxKey: 'starts_max',
- label: 'Starts',
- operatorText: 'is',
- type: FilterType.DateSelector,
- };
- const appliedFilters = {
- key: filter.maxKey,
- value: selectedDate,
- };
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- const firstTag = filterControl.findAll(Tag)[0];
- expect(firstTag).toContainReactText(`${filter.label} before INVALID`);
- });
-
- it('renders the provided applied filter string when filter uses operators with filter label', () => {
- const appliedFilterValue = '20';
- const appliedFilterKey = 'times_used';
- const appliedFilterLabel = 'is';
-
- const filter: FilterTextField = {
- key: 'filterKey1',
- label: 'Times used',
- operatorText: [
- {
- optionLabel: 'equal to',
- filterLabel: appliedFilterLabel,
- key: appliedFilterKey,
- },
- {
- optionLabel: 'not equal to',
- filterLabel: 'is not',
- key: 'times_used_not',
- },
- ],
- type: FilterType.TextField,
- };
- const appliedFilters = {
- key: appliedFilterKey,
- value: appliedFilterValue,
- };
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- const firstTag = filterControl.findAll(Tag)[0];
-
- expect(firstTag).toContainReactText(
- `${filter.label} ${appliedFilterLabel} ${appliedFilterValue}`,
- );
- });
-
- it('renders the provided applied filter string when filter uses operators without filter label', () => {
- const appliedFilterValue = '20';
- const appliedFilterKey = 'times_used';
- const appliedOperatorOptionLabel = 'equal to';
-
- const filter: FilterTextField = {
- key: 'filterKey1',
- label: 'Times used',
- operatorText: [
- {
- optionLabel: appliedOperatorOptionLabel,
- key: appliedFilterKey,
- },
- {
- optionLabel: 'not equal to',
- key: 'times_used_not',
- },
- ],
- type: FilterType.TextField,
- };
- const appliedFilters = {
- key: appliedFilterKey,
- value: appliedFilterValue,
- };
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- const firstTag = filterControl.findAll(Tag)[0];
-
- expect(firstTag).toContainReactText(
- `${filter.label} ${appliedOperatorOptionLabel} ${appliedFilterValue}`,
- );
- });
-
- it('renders the provided applied filter string when filter key cannot be found', () => {
- const appliedFilterValue = 'new Bundle';
- const appliedFilters = {
- key: 'filter key',
- value: appliedFilterValue,
- };
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- const firstTag = filterControl.findAll(Tag)[0];
- expect(firstTag).toContainReactText(appliedFilterValue);
- });
-
- it('renders a ul element containing an li element', () => {
- const appliedFilterValue = 'new Bundle';
- const appliedFilters = {
- key: 'filter key',
- value: appliedFilterValue,
- };
- const filterControl = mountWithApp(
-
-
- ,
- );
- expect(filterControl.findAll('ul')).toHaveLength(1);
- expect(filterControl.findAll('li')).toHaveLength(1);
- });
- });
-
- describe('additionalAction', () => {
- it('renders no connectedRight prop on TextField if there is no additionalAction', () => {
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- expect(filterControl).toContainReactComponent(TextField, {
- connectedRight: null,
- });
- });
-
- it('renders Button if there is additionalAction', () => {
- const action = {
- content: 'button label',
- onAction: jest.fn(),
- };
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- expect(filterControl).toContainReactComponent(Button);
-
- filterControl.find(Button)!.trigger('onClick');
- expect(action.onAction).toHaveBeenCalled();
- });
- });
-
- describe('focused', () => {
- it('passes its value to focus of TextField', () => {
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- expect(filterControl).toContainReactComponent(TextField, {focused: true});
- });
- });
-
- describe('filters', () => {
- it('renders no if there are no filters', () => {
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- expect(filterControl).toContainReactComponent(TextField, {
- connectedLeft: null,
- });
- });
-
- it('renders if there is filters', () => {
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- expect(filterControl).toContainReactComponent(FilterCreator);
- });
-
- it('renders with filters', () => {
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- expect(filterControl).toContainReactComponent(FilterCreator, {
- filters: mockFilters,
- });
- });
- });
-
- describe('placeholder', () => {
- it('renders default text if no placeholder passed.', () => {
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- expect(filterControl).toContainReactComponent(TextField, {
- placeholder: 'Search items',
- });
- });
-
- it('renders the placeholder prop value if provided', () => {
- const placeholder = 'Search by name, email or phone';
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- expect(filterControl).toContainReactComponent(TextField, {
- placeholder: 'Search by name, email or phone',
- });
- });
- });
-
- describe('onSearchBlur()', () => {
- it('gets passed to onBlur of TextField', () => {
- const onSearchBlur = jest.fn();
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- expect(filterControl).toContainReactComponent(TextField, {
- onBlur: onSearchBlur,
- });
- });
-
- it('calls onSearchBlur when onBlur is triggered', () => {
- const onSearchBlur = jest.fn();
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- filterControl.find(TextField)!.trigger('onBlur');
-
- expect(onSearchBlur).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('onSearchChange()', () => {
- it('gets passed to onChange of TextField', () => {
- const onSearchChange = jest.fn();
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- expect(filterControl).toContainReactComponent(TextField, {
- onChange: onSearchChange,
- });
- });
-
- it('calls onSearchChange with the new searchValue when onChange is triggered', () => {
- const newSearchValue = 'new search value';
- const onSearchChange = jest.fn();
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- filterControl.find(TextField)!.trigger('onChange', newSearchValue);
-
- expect(onSearchChange).toHaveBeenCalledWith(newSearchValue);
- });
- });
-
- describe('onFiltersChange()', () => {
- it('gets call with the new filter when FilterCreator.onAddFilter is triggered', () => {
- const newFilter = {
- key: 'new key',
- value: 'new value',
- };
-
- const onFiltersChange = jest.fn();
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- filterControl.find(FilterCreator)!.trigger('onAddFilter', newFilter);
-
- expect(onFiltersChange).toHaveBeenCalledWith([
- ...mockAppliedFilters,
- newFilter,
- ]);
- });
-
- it('does not get called if the new filter already exists when FilterCreator.onAddFilter is triggered', () => {
- const newFilter = mockAppliedFilters[0];
- const onFiltersChange = jest.fn();
- const filterControl = mountWithApp(
-
-
- ,
- );
-
- filterControl.find(FilterCreator)!.trigger('onAddFilter', newFilter);
-
- expect(onFiltersChange).not.toHaveBeenCalled();
- });
- });
-
- describe('resource name', () => {
- it('will default to the provided translation', () => {
- const filterControl = mountWithApp(
- ,
- );
-
- expect(filterControl).toContainReactComponent(TextField, {
- label: 'Search items',
- });
- });
- });
-});
-
-function noop() {}
diff --git a/src/components/ResourceList/components/FilterControl/types.ts b/src/components/ResourceList/components/FilterControl/types.ts
deleted file mode 100644
index 34aacb38324..00000000000
--- a/src/components/ResourceList/components/FilterControl/types.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-/* eslint-disable @typescript-eslint/consistent-indexed-object-style */
-import type {SelectOption} from '../../../Select';
-import type {TextFieldProps} from '../../../TextField';
-
-export interface Operator {
- key: string;
- optionLabel: string;
- filterLabel?: string;
-}
-
-export interface AppliedFilter {
- key: string;
- value: string;
- label?: string;
-}
-
-export enum FilterType {
- Select,
- TextField,
- DateSelector,
-}
-
-export interface FilterBase> {
- label: string;
- key: keyof TFilterKeys | string;
- operatorText?: string | Operator[];
- type: FilterType;
-}
-
-export interface FilterSelect>
- extends FilterBase {
- type: FilterType.Select;
- options: SelectOption[];
-}
-
-export interface FilterTextField>
- extends FilterBase {
- type: FilterType.TextField;
- textFieldType?: TextFieldProps['type'];
-}
-
-export interface FilterDateSelector>
- extends FilterBase {
- type: FilterType.DateSelector;
- minKey: string;
- maxKey: string;
- dateOptionType?: 'past' | 'future' | 'full';
-}
-
-export type Filter> =
- | FilterSelect
- | FilterTextField
- | FilterDateSelector;
diff --git a/src/components/ResourceList/components/index.ts b/src/components/ResourceList/components/index.ts
deleted file mode 100644
index 94187017fae..00000000000
--- a/src/components/ResourceList/components/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './FilterControl';
diff --git a/src/components/TextField/TextField.scss b/src/components/TextField/TextField.scss
index 7191913ad44..9b7c5f95b80 100644
--- a/src/components/TextField/TextField.scss
+++ b/src/components/TextField/TextField.scss
@@ -227,6 +227,10 @@ $prefix-horizontal-spacing: var(--p-space-2);
}
}
+.Hidden {
+ visibility: hidden;
+}
+
.Spinner {
--pc-text-field-spinner-offset-large: calc(
var(--p-text-field-spinner-offset) + var(--p-border-width-1)
diff --git a/src/components/TextField/TextField.tsx b/src/components/TextField/TextField.tsx
index f31a8115993..cf5d46fd5ae 100644
--- a/src/components/TextField/TextField.tsx
+++ b/src/components/TextField/TextField.tsx
@@ -278,20 +278,22 @@ export function TextField({
const clearButtonVisible = normalizedValue !== '';
- const clearButtonMarkup =
- clearButtonVisible && clearButton ? (
-
- ) : null;
+ const clearButtonClassNames = classNames(
+ styles.ClearButton,
+ !clearButtonVisible && styles.Hidden,
+ );
+
+ const clearButtonMarkup = clearButton ? (
+
+ ) : null;
const handleNumberChange = useCallback(
(steps: number) => {
@@ -430,6 +432,7 @@ export function TextField({
pattern,
inputMode,
type: inputType,
+ rows: getRows(multiline),
'aria-describedby': describedBy.length ? describedBy.join(' ') : undefined,
'aria-labelledby': labelledBy.join(' '),
'aria-invalid': Boolean(error),
@@ -529,3 +532,9 @@ function normalizeAriaMultiline(multiline?: boolean | number) {
? {'aria-multiline': true}
: undefined;
}
+
+function getRows(multiline?: boolean | number) {
+ if (!multiline) return undefined;
+
+ return typeof multiline === 'number' ? multiline : 1;
+}
diff --git a/src/components/TextStyle/README.md b/src/components/TextStyle/README.md
index f882432d2c7..b4b471d0f82 100644
--- a/src/components/TextStyle/README.md
+++ b/src/components/TextStyle/README.md
@@ -11,6 +11,7 @@ keywords:
- subdued
- strong
- negative
+ - warning
- positive
- cues
- enhancements
@@ -37,6 +38,7 @@ Text style should be:
- Used when enhancing the text to help merchants understand its meaning
- Subdued if the text is less important than its surrounding text
+- Warning if the text denotes something that needs attention, or that merchants need to take action on.
- Strong for input fields, or for a row total in a price table
- Paired with symbols, like an arrow or dollar sign, when using positive or negative styles
@@ -124,6 +126,14 @@ Use in combination with a symbol showing a decreasing value to indicate a downwa
+### Warning text style
+
+Use to denote something that needs attention, or that merchants need to take action on.
+
+```jsx
+Scheduled maintenance
+```
+
### Code text style
Use to display inline snippets of code or code-like text.
diff --git a/src/components/TextStyle/TextStyle.scss b/src/components/TextStyle/TextStyle.scss
index 73da1ef7246..281e6e52cd4 100644
--- a/src/components/TextStyle/TextStyle.scss
+++ b/src/components/TextStyle/TextStyle.scss
@@ -8,6 +8,10 @@
color: var(--p-text-critical);
}
+.variationWarning {
+ color: var(--p-text-warning);
+}
+
.variationCode {
position: relative;
padding: 0 var(--p-space-1);
diff --git a/src/components/TextStyle/TextStyle.tsx b/src/components/TextStyle/TextStyle.tsx
index 4be738b6edd..a77543d26b6 100644
--- a/src/components/TextStyle/TextStyle.tsx
+++ b/src/components/TextStyle/TextStyle.tsx
@@ -4,11 +4,18 @@ import {classNames, variationName} from '../../utilities/css';
import styles from './TextStyle.scss';
-type Variation = 'positive' | 'negative' | 'strong' | 'subdued' | 'code';
+type Variation =
+ | 'positive'
+ | 'negative'
+ | 'warning'
+ | 'strong'
+ | 'subdued'
+ | 'code';
enum VariationValue {
Positive = 'positive',
Negative = 'negative',
+ Warning = 'warning',
Strong = 'strong',
Subdued = 'subdued',
Code = 'code',
diff --git a/src/components/TextStyle/tests/TextStyle.test.tsx b/src/components/TextStyle/tests/TextStyle.test.tsx
index ddf8c6e2b52..6502d5b6ba5 100644
--- a/src/components/TextStyle/tests/TextStyle.test.tsx
+++ b/src/components/TextStyle/tests/TextStyle.test.tsx
@@ -33,6 +33,13 @@ describe('', () => {
expect(textStyle).toContainReactComponent('span');
});
+ it('renders a span when the variant warning is provided', () => {
+ const textStyle = mountWithApp(
+ Hello Polaris,
+ );
+ expect(textStyle).toContainReactComponent('span');
+ });
+
it('renders a span when the variant subdued is provided', () => {
const textStyle = mountWithApp(
Hello Polaris,
diff --git a/src/index.ts b/src/index.ts
index 03ac2d9ff41..3e1fb9aa1ef 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -277,12 +277,6 @@ export type {ResourceItemProps} from './components/ResourceItem';
export {ResourceList} from './components/ResourceList';
export type {ResourceListProps} from './components/ResourceList';
-// We want to delete this component, but there's one place in web
-// that still relies on it so we have to keep it.
-// We don't want anyone else using it though, use the Filters component instead
-export {FilterControl as _SECRET_INTERNAL_FilterControl} from './components/ResourceList';
-export type {FilterControlProps as _SECRET_INTERNAL_FilterControlProps} from './components/ResourceList';
-
export {Scrollable} from './components/Scrollable';
export type {ScrollableProps} from './components/Scrollable';
diff --git a/src/styles/shared/_page.scss b/src/styles/shared/_page.scss
index 3a857e4c58e..3f98d7ce14c 100644
--- a/src/styles/shared/_page.scss
+++ b/src/styles/shared/_page.scss
@@ -26,7 +26,7 @@ $actions-vertical-spacing: var(--p-space-2);
margin: var(--p-space-2) 0;
@include page-content-when-not-partially-condensed {
- margin-top: var(--p-space-2);
+ margin-top: var(--p-space-5);
}
}
@@ -46,7 +46,7 @@ $actions-vertical-spacing: var(--p-space-2);
}
@mixin page-header-layout {
- padding: var(--p-space-4) var(--p-space-4) 0;
+ padding: var(--p-space-4);
@include page-content-when-not-fully-condensed {
padding-left: 0;
@@ -54,7 +54,7 @@ $actions-vertical-spacing: var(--p-space-2);
}
@include page-content-when-not-partially-condensed {
- padding-top: var(--p-space-4);
+ padding: var(--p-space-5) 0;
}
}
diff --git a/src/tokens/token-groups/shape.json b/src/tokens/token-groups/shape.json
index 27508fcebb6..c236ef273f0 100644
--- a/src/tokens/token-groups/shape.json
+++ b/src/tokens/token-groups/shape.json
@@ -15,5 +15,5 @@
"border-base": "var(--p-border-width-1) solid var(--p-border-subdued)",
"border-dark": "var(--p-border-width-1) solid var(--p-border)",
"border-transparent": "var(--p-border-width-1) solid transparent",
- "border-divider": "var(--p-border-width-1) solid var(--p-divider)"
+ "border-divider": "var(--p-border-width-1) solid var(--p-border-divider)"
}
diff --git a/src/utilities/autocomplete/context.ts b/src/utilities/autocomplete/context.ts
index 0030be0b1f3..93ef142ceb9 100644
--- a/src/utilities/autocomplete/context.ts
+++ b/src/utilities/autocomplete/context.ts
@@ -6,9 +6,6 @@ interface MappedActionContextType {
external?: boolean;
onAction?(): void;
destructive?: boolean;
- isAction: boolean;
}
-export const MappedActionContext = createContext({
- isAction: false,
-});
+export const MappedActionContext = createContext({});
diff --git a/src/utilities/listbox/context.ts b/src/utilities/listbox/context.ts
index f64256aa8ed..e4451cc74a8 100644
--- a/src/utilities/listbox/context.ts
+++ b/src/utilities/listbox/context.ts
@@ -12,3 +12,5 @@ export const ListboxContext = createContext(
);
export const WithinListboxContext = createContext(false);
+
+export const ActionContext = createContext(false);
diff --git a/yarn.lock b/yarn.lock
index 1a1e9eee788..ca9a8aba164 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2015,10 +2015,10 @@
fs-extra "^9.0.0"
glob "^7.1.6"
-"@shopify/polaris-icons@^4.11.0":
- version "4.11.0"
- resolved "https://registry.yarnpkg.com/@shopify/polaris-icons/-/polaris-icons-4.11.0.tgz#8b1ebf7f07077300f8281e12824588835f108b23"
- integrity sha512-vq37+I6oDlFzQXqkuDI0DO4AXz36AiyGOMkSstnBWTI6UR/eHAuI2aALqbPMxYbD6877Pi1kk+bAfWIFf3D4LA==
+"@shopify/polaris-icons@4.14.0":
+ version "4.14.0"
+ resolved "https://registry.yarnpkg.com/@shopify/polaris-icons/-/polaris-icons-4.14.0.tgz#26d134af7fda64e81ecb97212ff5f424a8a6774a"
+ integrity sha512-UyNcPmR/emsL01ezXR87U++9fLANaBiZZtG55XBe92cDeiG+VmVnx/a75U9wEL9jlDGjWNtO1Jd7juq0r/EHLQ==
"@shopify/postcss-plugin@^5.0.1":
version "5.0.1"
@@ -4270,11 +4270,6 @@
resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
-"@zeit/schemas@2.6.0":
- version "2.6.0"
- resolved "https://registry.yarnpkg.com/@zeit/schemas/-/schemas-2.6.0.tgz#004e8e553b4cd53d538bd38eac7bcbf58a867fe3"
- integrity sha512-uUrgZ8AxS+Lio0fZKAipJjAh415JyrOZowliZAzmnJSsf7piVL5w+G0+gFJ0KSu3QRhvui/7zuvpLz03YjXAhg==
-
abab@^2.0.3, abab@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a"
@@ -4397,7 +4392,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2:
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
-ajv@6.12.6, ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5:
+ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -4437,13 +4432,6 @@ amdefine@>=0.0.4:
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=
-ansi-align@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f"
- integrity sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=
- dependencies:
- string-width "^2.0.0"
-
ansi-align@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb"
@@ -4561,11 +4549,6 @@ aproba@^1.0.3, aproba@^1.1.1:
resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==
-arch@^2.1.1:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11"
- integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==
-
are-we-there-yet@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c"
@@ -4582,11 +4565,6 @@ are-we-there-yet@~1.1.2:
delegates "^1.0.0"
readable-stream "^2.0.6"
-arg@2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/arg/-/arg-2.0.0.tgz#c06e7ff69ab05b3a4a03ebe0407fac4cba657545"
- integrity sha512-XxNTUzKnz1ctK3ZIcI2XUPlD96wbHP2nGqkPKpvk/HNRlPveYrXIVSTk9m3LcqOgDPg3B1nMvdV/K8wZd7PG4w==
-
argparse@^1.0.7:
version "1.0.10"
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
@@ -5150,19 +5128,6 @@ boolbase@^1.0.0, boolbase@~1.0.0:
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
-boxen@1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b"
- integrity sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==
- dependencies:
- ansi-align "^2.0.0"
- camelcase "^4.0.0"
- chalk "^2.0.1"
- cli-boxes "^1.0.0"
- string-width "^2.0.0"
- term-size "^1.2.0"
- widest-line "^2.0.0"
-
boxen@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50"
@@ -5483,11 +5448,6 @@ camelcase-keys@^6.2.2:
map-obj "^4.0.0"
quick-lru "^4.0.1"
-camelcase@^4.0.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
- integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
-
camelcase@^5.0.0, camelcase@^5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
@@ -5549,16 +5509,7 @@ ccount@^1.0.0:
resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.5.tgz#ac82a944905a65ce204eb03023157edf29425c17"
integrity sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw==
-chalk@2.4.1:
- version "2.4.1"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e"
- integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==
- dependencies:
- ansi-styles "^3.2.1"
- escape-string-regexp "^1.0.5"
- supports-color "^5.3.0"
-
-chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.1, chalk@^2.4.2:
+chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -5759,11 +5710,6 @@ clean-stack@^2.0.0:
resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
-cli-boxes@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143"
- integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM=
-
cli-boxes@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f"
@@ -5797,15 +5743,6 @@ clipboard@^2.0.0:
select "^1.1.2"
tiny-emitter "^2.0.0"
-clipboardy@2.3.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-2.3.0.tgz#3c2903650c68e46a91b388985bc2774287dba290"
- integrity sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==
- dependencies:
- arch "^2.1.1"
- execa "^1.0.0"
- is-wsl "^2.1.1"
-
cliui@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5"
@@ -5985,26 +5922,13 @@ component-emitter@^1.2.1:
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
-compressible@~2.0.14, compressible@~2.0.16:
+compressible@~2.0.16:
version "2.0.18"
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==
dependencies:
mime-db ">= 1.43.0 < 2"
-compression@1.7.3:
- version "1.7.3"
- resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.3.tgz#27e0e176aaf260f7f2c2813c3e440adb9f1993db"
- integrity sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==
- dependencies:
- accepts "~1.3.5"
- bytes "3.0.0"
- compressible "~2.0.14"
- debug "2.6.9"
- on-headers "~1.0.1"
- safe-buffer "5.1.2"
- vary "~1.1.2"
-
compression@^1.7.4:
version "1.7.4"
resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f"
@@ -6072,11 +5996,6 @@ constants-browserify@^1.0.0:
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=
-content-disposition@0.5.2:
- version "0.5.2"
- resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"
- integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ=
-
content-disposition@0.5.3:
version "0.5.3"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd"
@@ -6279,15 +6198,6 @@ cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"
-cross-spawn@^5.0.1:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
- integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=
- dependencies:
- lru-cache "^4.0.1"
- shebang-command "^1.2.0"
- which "^1.2.9"
-
cross-spawn@^6.0.0, cross-spawn@^6.0.5:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
@@ -7558,19 +7468,6 @@ exec-sh@^0.3.2:
resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.2.tgz#6738de2eb7c8e671d0366aea0b0db8c6f7d7391b"
integrity sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==
-execa@^0.7.0:
- version "0.7.0"
- resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
- integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=
- dependencies:
- cross-spawn "^5.0.1"
- get-stream "^3.0.0"
- is-stream "^1.1.0"
- npm-run-path "^2.0.0"
- p-finally "^1.0.0"
- signal-exit "^3.0.0"
- strip-eof "^1.0.0"
-
execa@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
@@ -7804,13 +7701,6 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6:
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
-fast-url-parser@1.1.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d"
- integrity sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0=
- dependencies:
- punycode "^1.3.2"
-
fastest-levenshtein@^1.0.12:
version "1.0.12"
resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2"
@@ -8281,11 +8171,6 @@ get-stdin@^8.0.0:
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53"
integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==
-get-stream@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
- integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=
-
get-stream@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
@@ -10598,14 +10483,6 @@ lowlight@~1.11.0:
fault "^1.0.2"
highlight.js "~9.13.0"
-lru-cache@^4.0.1:
- version "4.1.5"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
- integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
- dependencies:
- pseudomap "^1.0.2"
- yallist "^2.1.2"
-
lru-cache@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
@@ -10890,18 +10767,6 @@ mime-db@1.44.0, "mime-db@>= 1.43.0 < 2":
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
-mime-db@~1.33.0:
- version "1.33.0"
- resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db"
- integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==
-
-mime-types@2.1.18:
- version "2.1.18"
- resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8"
- integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==
- dependencies:
- mime-db "~1.33.0"
-
mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19, mime-types@~2.1.24:
version "2.1.27"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
@@ -11583,7 +11448,7 @@ on-finished@~2.3.0:
dependencies:
ee-first "1.1.1"
-on-headers@~1.0.1, on-headers@~1.0.2:
+on-headers@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
@@ -11959,11 +11824,6 @@ path-is-absolute@^1.0.0:
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
-path-is-inside@1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
- integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=
-
path-key@^2.0.0, path-key@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
@@ -11984,7 +11844,7 @@ path-to-regexp@0.1.7:
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
-path-to-regexp@2.2.1, path-to-regexp@^2.2.1:
+path-to-regexp@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45"
integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==
@@ -12689,11 +12549,6 @@ prr@~1.0.1:
resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY=
-pseudomap@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
- integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
-
psl@^1.1.28, psl@^1.1.33:
version "1.8.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
@@ -12740,7 +12595,7 @@ punycode@1.3.2:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
-punycode@^1.2.4, punycode@^1.3.2:
+punycode@^1.2.4:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
@@ -12817,11 +12672,6 @@ randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.1.0:
dependencies:
safe-buffer "^5.1.0"
-range-parser@1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
- integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=
-
range-parser@^1.2.1, range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
@@ -12845,7 +12695,7 @@ raw-loader@^4.0.2:
loader-utils "^2.0.0"
schema-utils "^3.0.0"
-rc@^1.0.1, rc@^1.1.6, rc@^1.2.7:
+rc@^1.2.7:
version "1.2.8"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
@@ -13344,21 +13194,6 @@ regexpu-core@^4.7.1:
unicode-match-property-ecmascript "^1.0.4"
unicode-match-property-value-ecmascript "^1.2.0"
-registry-auth-token@3.3.2:
- version "3.3.2"
- resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20"
- integrity sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==
- dependencies:
- rc "^1.1.6"
- safe-buffer "^5.0.1"
-
-registry-url@3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942"
- integrity sha1-PU74cPc93h138M+aOBQyRE4XSUI=
- dependencies:
- rc "^1.0.1"
-
regjsgen@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c"
@@ -13844,20 +13679,6 @@ serve-favicon@^2.5.0:
parseurl "~1.3.2"
safe-buffer "5.1.1"
-serve-handler@6.1.3:
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.3.tgz#1bf8c5ae138712af55c758477533b9117f6435e8"
- integrity sha512-FosMqFBNrLyeiIDvP1zgO6YoTzFYHxLDEIavhlmQ+knB2Z7l1t+kGLHkZIDN7UVWqQAmKI3D20A6F6jo3nDd4w==
- dependencies:
- bytes "3.0.0"
- content-disposition "0.5.2"
- fast-url-parser "1.1.3"
- mime-types "2.1.18"
- minimatch "3.0.4"
- path-is-inside "1.0.2"
- path-to-regexp "2.2.1"
- range-parser "1.2.0"
-
serve-static@1.14.1:
version "1.14.1"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
@@ -13868,21 +13689,6 @@ serve-static@1.14.1:
parseurl "~1.3.3"
send "0.17.1"
-serve@^12.0.0:
- version "12.0.0"
- resolved "https://registry.yarnpkg.com/serve/-/serve-12.0.0.tgz#122962f712b57660059de9d109c82599280e4949"
- integrity sha512-BkTsETQYynAZ7rXX414kg4X6EvuZQS3UVs1NY0VQYdRHSTYWPYcH38nnDh48D0x6ONuislgjag8uKlU2gTBImA==
- dependencies:
- "@zeit/schemas" "2.6.0"
- ajv "6.12.6"
- arg "2.0.0"
- boxen "1.3.0"
- chalk "2.4.1"
- clipboardy "2.3.0"
- compression "1.7.3"
- serve-handler "6.1.3"
- update-check "1.5.2"
-
set-blocking@^2.0.0, set-blocking@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
@@ -14375,7 +14181,7 @@ string-width@^1.0.1:
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
-"string-width@^1.0.1 || ^2.0.0", "string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1:
+"string-width@^1.0.1 || ^2.0.0", "string-width@^1.0.2 || 2":
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
@@ -14858,13 +14664,6 @@ telejson@^5.3.2, telejson@^5.3.3:
lodash "^4.17.21"
memoizerific "^1.11.3"
-term-size@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69"
- integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=
- dependencies:
- execa "^0.7.0"
-
terminal-link@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994"
@@ -15459,14 +15258,6 @@ upath@^1.1.1:
resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068"
integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==
-update-check@1.5.2:
- version "1.5.2"
- resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.5.2.tgz#2fe09f725c543440b3d7dabe8971f2d5caaedc28"
- integrity sha512-1TrmYLuLj/5ZovwUS7fFd1jMH3NnFDN1y1A8dboedIDt7zs/zJMo6TwwlhYKkSeEwzleeiSBV5/3c9ufAQWDaQ==
- dependencies:
- registry-auth-token "3.3.2"
- registry-url "3.1.0"
-
upper-case-first@^1.1.0, upper-case-first@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115"
@@ -15948,13 +15739,6 @@ wide-align@^1.1.2:
dependencies:
string-width "^1.0.2 || 2 || 3 || 4"
-widest-line@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc"
- integrity sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==
- dependencies:
- string-width "^2.1.1"
-
widest-line@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca"
@@ -16078,11 +15862,6 @@ yaeti@^0.0.6:
resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577"
integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=
-yallist@^2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
- integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
-
yallist@^3.0.0, yallist@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9"