Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PageActions] Add ReactNode as an accepted secondaryActions prop value for customizability #5495

Merged
merged 1 commit into from
Apr 12, 2022

Conversation

stefanlegg
Copy link
Contributor

@stefanlegg stefanlegg commented Apr 11, 2022

WHY are these changes introduced?

This PR is related to a similar recent change to secondaryActions on Page.

Currently secondaryActions on PageActions only allows for passing props as ComplexAction[] which doesn't allow for flexibility and results in accessibility issues in some cases.

In cases where secondary actions open a modal we are unable to move focus back to the button that opened it after the modal is closed. With this change to accept React.ReactNode we are better able to support keyboard users through passing an entire modal as secondary action to leverage modal's built-in focus management, or through passing a custom button that'd able to have a ref to receive the focus.

WHAT is this pull request doing?

This PR maintains the existing secondaryActions prop, while adding the option to pass a ReactNode instead for customizability.

How to 🎩

🖥 Local development instructions
🗒 General tophatting guidelines
📄 Changelog guidelines

Copy-paste this code in playground/Playground.tsx:
import React, {useCallback, useState} from 'react';

import {Button, Modal, Page, PageActions, TextContainer} from '../src';

export function Playground() {
  return (
    <Page>
      <PageActions 
        primaryAction={{
          content: 'Save',
        }} 
        secondaryActions={<ModalExample />}
      />
    </Page>
  );
}

function ModalExample() {
  const [active, setActive] = useState(true);

  const handleChange = useCallback(() => setActive(!active), [active]);

  const activator = <Button onClick={handleChange}>Open</Button>;

  return (
    <Modal
      activator={activator}
      open={active}
      onClose={handleChange}
      title="Reach more shoppers with Instagram product tags"
      primaryAction={{
        content: 'Add Instagram',
        onAction: handleChange,
      }}
      secondaryActions={[
        {
          content: 'Learn more',
          onAction: handleChange,
        },
      ]}
    >
      <Modal.Section>
        <TextContainer>
          <p>
            Use Instagram posts to share your products with millions of people.
            Let shoppers buy from your store without leaving Instagram.
          </p>
        </TextContainer>
      </Modal.Section>
    </Modal>
  );
}

🎩 checklist

@github-actions
Copy link
Contributor

github-actions bot commented Apr 11, 2022

size-limit report 📦

Path Size
cjs 158.51 KB (+0.02% 🔺)
esm 89.73 KB (+0.02% 🔺)
esnext 139.1 KB (+0.01% 🔺)
css 36.63 KB (0%)

@stefanlegg stefanlegg self-assigned this Apr 11, 2022
Comment on lines 1 to 9
import {isValidElement} from 'react';

export function isInterface<T>(x: T | React.ReactNode): x is T {
return !isValidElement(x) && x !== undefined;
}

export function isReactElement<T>(x: T): x is T {
return isValidElement(x) && x !== undefined;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

[question] These are the same util functions that are present for the Page component. Is there an appropriate place we could refactor both copies into? Maybe utilities/components.tsx?

Copy link
Contributor

Choose a reason for hiding this comment

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

The utilities/components.tsx file looks like a good place to me for such utilities if used globally for reusability, could polaris team confirm if our assumption is correct? 🙏

Copy link
Member

Choose a reason for hiding this comment

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

Yes that's a good idea, you can make individual files named similarly to is-object.ts 👍🏽

@stefanlegg stefanlegg marked this pull request as ready for review April 11, 2022 19:58
Copy link
Contributor

@tatianau tatianau left a comment

Choose a reason for hiding this comment

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

Thanks for doing this, changes look good to me! 🚀
Let's wait for polaris team members to ✅ 🙏

Comment on lines 1 to 9
import {isValidElement} from 'react';

export function isInterface<T>(x: T | React.ReactNode): x is T {
return !isValidElement(x) && x !== undefined;
}

export function isReactElement<T>(x: T): x is T {
return isValidElement(x) && x !== undefined;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The utilities/components.tsx file looks like a good place to me for such utilities if used globally for reusability, could polaris team confirm if our assumption is correct? 🙏

Copy link
Contributor

@zaquille-oneil zaquille-oneil left a comment

Choose a reason for hiding this comment

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

LGTM!

<Button
connectedDisclosure={{
accessibilityLabel: 'Other save actions',
actions: [{content: 'Save as new'}],
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
actions: [{content: 'Save as new'}],
actions: [{content: 'Save as draft'}],

@@ -8,6 +8,8 @@ Use [the changelog guidelines](/documentation/Versioning%20and%20changelog.md) t

- Added `icon` prop to the `Badge` component ([#5292](https://github.com/Shopify/polaris/pull/5292))

- Added `ReactNode` as an accepted prop type to `secondaryActions` on the `PageActions` component ([#5495](https://github.com/Shopify/polaris/pull/5495))
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
- Added `ReactNode` as an accepted prop type to `secondaryActions` on the `PageActions` component ([#5495](https://github.com/Shopify/polaris/pull/5495))
- Added support for setting a `ReactNode` on the `PageActions` `secondaryActions` prop ([#5495](https://github.com/Shopify/polaris/pull/5495))

Comment on lines 1 to 9
import {isValidElement} from 'react';

export function isInterface<T>(x: T | React.ReactNode): x is T {
return !isValidElement(x) && x !== undefined;
}

export function isReactElement<T>(x: T): x is T {
return isValidElement(x) && x !== undefined;
}
Copy link
Member

Choose a reason for hiding this comment

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

Yes that's a good idea, you can make individual files named similarly to is-object.ts 👍🏽

actions: [{content: 'Save as new'}],
}}
>
Save as new
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Save as new
Save

@stefanlegg stefanlegg merged commit bf787c9 into main Apr 12, 2022
@stefanlegg stefanlegg deleted the sl-custom-secondary-actions branch April 12, 2022 17:46
@ghost
Copy link

ghost commented Apr 12, 2022

🎉 Thanks for your contribution to Polaris React!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants