Skip to content

Commit

Permalink
ActionsList filtering (#9907)
Browse files Browse the repository at this point in the history
Closes #8686

## Why?
With extension GA comes a lot of potential new items in the `More
Actions` of pages supporting extensibility making it increasingly more
difficult for merchants to find what they are looking for.
The median number of links in "More action" menus on the products,
orders, and customers pages for high GMV merchants is already between 12
and 31. This number does not count any non-app actions that have been
added, so in reality, the number of actions is even higher. This is a
terrible UX for our merchants.

## Proposed Changes

Adding a search input that will appear anytime an ActionList has more
than 10 elements.

<img width="1015" alt="Screenshot 2023-08-09 at 9 06 13 AM"
src="https://github.com/Shopify/ui-api-design/assets/3555826/cb021493-ada4-4ac2-8991-42a6d979207a">
<img width="1261" alt="Screenshot 2023-08-10 at 1 52 48 PM"
src="https://github.com/Shopify/polaris/assets/3555826/6c2d9763-6b81-4808-9659-eb1db7b4bf1c">


This will give us the filtering behaviour in various areas and cases for
free:
- When actionGroups are rolledUp
- In Bulk Actions
- When actionGroups are not rolledUp, but still have more than 10 items

This will also enable the rest of the admin to leverage this behaviour
for free to give a consistent merchant experience.

## Open questions

Using the TextField (or SearchField from filters) creates a circular
dependency with the ActionList. What would be the right approach to have
the search input with an icon without creating circular dependencies?

## Resources

- Action Extensions tech doc -
https://docs.google.com/document/d/1BEAHpkfeyUTaxW3B18LjojimDmqNLr1Fc2wfpMXzQ64/edit
- Mockups for Action Extensions -
https://www.figma.com/proto/afrUykUTB0n4GB4evE5F6R/%F0%9F%92%8EAdmin-UI-extension-patterns?node-id=398-111475&scaling=scale-down&page-id=295%3A118673&starting-point-node-id=398%3A111475&show-proto-sidebar=1
- Post in Admin UX (WxM) with an initial proposal -
https://shopify.workplace.com/groups/1294395484626415/permalink/1321299791935984/
- Investigation of changes needed -
Shopify/app-ui#36 (comment)

---------

Co-authored-by: translation-platform[bot] <34770790+translation-platform[bot]@users.noreply.github.com>
  • Loading branch information
MaxCloutier and translation-platform[bot] authored Aug 24, 2023
1 parent 295feef commit ef7ddb4
Show file tree
Hide file tree
Showing 16 changed files with 632 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .changeset/curly-chairs-speak.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/polaris': minor
---

Add a search field to filter ActionList that have more than 10 items
7 changes: 7 additions & 0 deletions polaris-react/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,13 @@
"cancel": "Abbrechen"
}
}
},
"ActionList": {
"SearchField": {
"clearButtonLabel": "Löschen",
"search": "Suchen",
"placeholder": "Aktionen durchsuchen"
}
}
}
}
7 changes: 7 additions & 0 deletions polaris-react/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@
"rollupButton": "View actions"
}
},
"ActionList": {
"SearchField": {
"clearButtonLabel": "Clear",
"search": "Search",
"placeholder": "Search actions"
}
},
"Avatar": {
"label": "Avatar",
"labelWithInitials": "Avatar with initials {initials}"
Expand Down
7 changes: 7 additions & 0 deletions polaris-react/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,13 @@
"cancel": "Cancelar"
}
}
},
"ActionList": {
"SearchField": {
"clearButtonLabel": "Borrar",
"search": "Búsqueda",
"placeholder": "Buscar acciones"
}
}
}
}
7 changes: 7 additions & 0 deletions polaris-react/locales/nb.json
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,13 @@
"cancel": "Avbryt"
}
}
},
"ActionList": {
"SearchField": {
"clearButtonLabel": "Tøm",
"search": "Søk",
"placeholder": "Søk i handlinger"
}
}
}
}
7 changes: 7 additions & 0 deletions polaris-react/locales/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,13 @@
"cancel": "Anuluj"
}
}
},
"ActionList": {
"SearchField": {
"clearButtonLabel": "Wyczyść",
"search": "Szukaj",
"placeholder": "Szukaj czynności"
}
}
}
}
7 changes: 7 additions & 0 deletions polaris-react/locales/pt-BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,13 @@
"cancel": "Cancelar"
}
}
},
"ActionList": {
"SearchField": {
"clearButtonLabel": "Limpar",
"search": "Pesquisar",
"placeholder": "Pesquisar ações"
}
}
}
}
7 changes: 7 additions & 0 deletions polaris-react/locales/pt-PT.json
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,13 @@
"cancel": "Cancelar"
}
}
},
"ActionList": {
"SearchField": {
"clearButtonLabel": "Limpar",
"search": "Pesquisar",
"placeholder": "Pesquisar ações"
}
}
}
}
1 change: 0 additions & 1 deletion polaris-react/playground/DetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,6 @@ export function DetailsPage() {
actions: [
{
content: 'Embed on a website',

onAction: () => console.log('embed'),
},
{
Expand Down
72 changes: 57 additions & 15 deletions polaris-react/src/components/ActionList/ActionList.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import React, {useRef} from 'react';
import React, {useMemo, useRef, useState} from 'react';

import type {ActionListItemDescriptor, ActionListSection} from '../../types';
import {Key} from '../../types';
import {
wrapFocusNextFocusableMenuItem,
wrapFocusPreviousFocusableMenuItem,
} from '../../utilities/focus';
import {KeypressListener} from '../KeypressListener';
import {Key} from '../../types';
import type {ActionListItemDescriptor, ActionListSection} from '../../types';
import {Box} from '../Box';
import {KeypressListener} from '../KeypressListener';
import {useI18n} from '../../utilities/i18n';

import {Section, Item} from './components';
import {SearchField, Item, Section} from './components';
import type {ItemProps} from './components';

export interface ActionListProps {
Expand All @@ -31,8 +32,11 @@ export function ActionList({
actionRole,
onActionAnyItem,
}: ActionListProps) {
const i18n = useI18n();

let finalSections: readonly ActionListSection[] = [];
const actionListRef = useRef<HTMLDivElement & HTMLUListElement>(null);
const [searchText, setSeachText] = useState('');

if (items) {
finalSections = [{items}, ...sections];
Expand All @@ -46,7 +50,14 @@ export function ActionList({
const elementTabIndex =
hasMultipleSections && actionRole === 'menuitem' ? -1 : undefined;

const sectionMarkup = finalSections.map((section, index) => {
const filteredSections = finalSections?.map((section) => ({
...section,
items: section.items.filter((item) =>
item.content?.toLowerCase().includes(searchText.toLowerCase()),
),
}));

const sectionMarkup = filteredSections.map((section, index) => {
return section.items.length > 0 ? (
<Section
key={typeof section.title === 'string' ? section.title : index}
Expand Down Expand Up @@ -99,16 +110,47 @@ export function ActionList({
</>
) : null;

const totalActions =
finalSections?.reduce(
(acc: number, section) => acc + section.items.length,
0,
) || 0;

const totalFilteredActions = useMemo(() => {
const totalSectionItems =
filteredSections?.reduce(
(acc: number, section) => acc + section.items.length,
0,
) || 0;

return totalSectionItems;
}, [filteredSections]);

const showSearch = totalActions >= 8;

return (
<Box
as={hasMultipleSections ? 'ul' : 'div'}
ref={actionListRef}
role={elementRole}
tabIndex={elementTabIndex}
>
{listeners}
{sectionMarkup}
</Box>
<>
{showSearch && (
<Box padding="2" paddingBlockEnd={totalFilteredActions > 0 ? '0' : '2'}>
<SearchField
placeholder={i18n.translate(
'Polaris.ActionList.SearchField.placeholder',
)}
value={searchText}
onChange={(value) => setSeachText(value)}
/>
</Box>
)}
<Box
as={hasMultipleSections ? 'ul' : 'div'}
ref={actionListRef}
role={elementRole}
tabIndex={elementTabIndex}
>
{listeners}
{sectionMarkup}
</Box>
</>
);
}

Expand Down
Loading

0 comments on commit ef7ddb4

Please sign in to comment.