diff --git a/UNRELEASED.md b/UNRELEASED.md index f8257737542..88241f43862 100644 --- a/UNRELEASED.md +++ b/UNRELEASED.md @@ -16,6 +16,7 @@ Use [the changelog guidelines](https://git.io/polaris-changelog-guidelines) to f - Converted `Form`, `Frame`, and `Loading` examples to functional components ([#2130](https://github.com/Shopify/polaris-react/pull/2130)) - Replaced Latin abbreviations with English words in Text field content guidelines ([#2192](https://github.com/Shopify/polaris-react/pull/2192)) +- Converted `SettingToggle`, `Sheet`, and `Tabs` examples to functional components ([#2134](https://github.com/Shopify/polaris-react/pull/2134)) ### Development workflow diff --git a/src/components/AccountConnection/README.md b/src/components/AccountConnection/README.md index e12f21a2457..56e443e8b51 100644 --- a/src/components/AccountConnection/README.md +++ b/src/components/AccountConnection/README.md @@ -106,50 +106,38 @@ Connect to app Use to let merchants connect or disconnect their store to their third-party accounts, like Facebook. ```jsx -class AccountConnectionExample extends React.Component { - state = { - connected: false, - accountName: '', - }; - - render() { - const {accountName, connected} = this.state; - const buttonText = connected ? 'Disconnect' : 'Connect'; - const details = connected ? 'Account connected' : 'No account connected'; - const terms = connected ? null : ( -

- By clicking Connect, you agree to accept Sample App’s{' '} - terms and conditions. You’ll pay a - commission rate of 15% on sales made through Sample App. -

- ); - - return ( - - ); - } - - handleAction = () => { - this.setState((state) => { - const connected = !state.connected; - const accountName = connected ? 'Jane Appleseed' : ''; - - return { - connected, - accountName, - }; - }); - }; +function AccountConnectionExample() { + const [connected, setConnected] = useState(false); + const accountName = connected ? 'Jane Appleseed' : ''; + + const handleAction = useCallback(() => { + const newConnected = !connected; + setConnected((connected) => !connected); + }, [connected]); + + const buttonText = connected ? 'Disconnect' : 'Connect'; + const details = connected ? 'Account connected' : 'No account connected'; + const terms = connected ? null : ( +

+ By clicking Connect, you agree to accept Sample App’s{' '} + terms and conditions. You’ll pay a + commission rate of 15% on sales made through Sample App. +

+ ); + + return ( + + ); } ``` diff --git a/src/components/ActionList/README.md b/src/components/ActionList/README.md index 91938a99c31..2213f63fb1e 100644 --- a/src/components/ActionList/README.md +++ b/src/components/ActionList/README.md @@ -85,51 +85,45 @@ Each item in an action list should be scannable avoiding unnecessary words and a Use for the least important actions so merchants aren’t distracted by secondary tasks. Can also be used for a set of actions that won’t fit in the available screen space. ```jsx -class ActionListExample extends React.Component { - state = { - active: true, - }; - - togglePopover = () => { - this.setState(({active}) => { - return {active: !active}; - }); - }; - - render() { - const activator = ( - - ); - - return ( -
- - { - console.log('File imported'); - }, - }, - { - content: 'Export file', - onAction: () => { - console.log('File exported'); - }, - }, - ]} - /> - -
- ); - } +function ActionListInPopoverExample() { + const [active, setActive] = useState(true); + + const toggleActive = useCallback(() => setActive((active) => !active), []); + + const handleImportedAction = useCallback( + () => console.log('Imported action'), + [], + ); + + const handleExportedAction = useCallback( + () => console.log('Exported action'), + [], + ); + + const activator = ( + + ); + + return ( +
+ + + +
+ ); } ``` @@ -138,41 +132,29 @@ class ActionListExample extends React.Component { Use when the items benefit from an associated action or image, such as a list of products. ```jsx -class ActionListExample extends React.Component { - state = { - active: true, - }; - - togglePopover = () => { - this.setState(({active}) => { - return {active: !active}; - }); - }; - - render() { - const activator = ( - - ); - - return ( -
- - - -
- ); - } +function ActionListWithMediaExample() { + const [active, setActive] = useState(true); + + const toggleActive = useCallback(() => setActive((active) => !active), []); + + const activator = ( + + ); + + return ( +
+ + + +
+ ); } ``` @@ -181,46 +163,34 @@ class ActionListExample extends React.Component { Use when the items benefit from sections to help differentiate actions. ```jsx -class ActionListExample extends React.Component { - state = { - active: true, - }; - - togglePopover = () => { - this.setState(({active}) => { - return {active: !active}; - }); - }; - - render() { - const activator = ( - - ); - - return ( -
- - - -
- ); - } +function SectionedActionListExample() { + const [active, setActive] = useState(true); + + const toggleActive = useCallback(() => setActive((active) => !active), []); + + const activator = ( + + ); + + return ( +
+ + + +
+ ); } ``` @@ -229,51 +199,39 @@ class ActionListExample extends React.Component { Use to visually indicate that an action list item is destructive. ```jsx -class ActionListExample extends React.Component { - state = { - active: true, - }; - - togglePopover = () => { - this.setState(({active}) => { - return {active: !active}; - }); - }; - - render() { - const activator = ( - - ); - - return ( -
- - - -
- ); - } +function ActionListWithDestructiveItemExample() { + const [active, setActive] = useState(true); + + const toggleActive = useCallback(() => setActive((active) => !active), []); + + const activator = ( + + ); + + return ( +
+ + + +
+ ); } ``` @@ -282,51 +240,39 @@ class ActionListExample extends React.Component { Use help text when the normal Verb noun syntax for the actions does not provide sufficient context for the merchant. ```jsx -class ActionListExample extends React.Component { - state = { - active: true, - }; - - togglePopover = () => { - this.setState(({active}) => { - return {active: !active}; - }); - }; - - render() { - const activator = ( - - ); - - return ( -
- - - -
- ); - } +function ActionListWithHelpTextExample() { + const [active, setActive] = useState(true); + + const toggleActive = useCallback(() => setActive((active) => !active), []); + + const activator = ( + + ); + + return ( +
+ + + +
+ ); } ``` diff --git a/src/components/AppProvider/README.md b/src/components/AppProvider/README.md index 0d657cb6761..e04270be89e 100644 --- a/src/components/AppProvider/README.md +++ b/src/components/AppProvider/README.md @@ -158,47 +158,45 @@ With an `i18n`, `AppProvider` will provide these translations to polaris compone With a `linkComponent`, the app provider component will override the links used in other components. For example you may want to use the `Link` component provided by `react-router` throughout your application instead of the default `a` tag. ```jsx -class ProviderLinkExample extends React.Component { - render() { - const CustomLinkComponent = ({children, url, ...rest}) => { - return ( - console.log('Custom link clicked')} - {...rest} - > - {children} - - ); - }; - +function AppProviderLinkExample() { + const CustomLinkComponent = ({children, url, ...rest}) => { return ( - console.log('Custom link clicked')} + {...rest} + > + {children} + + ); + }; + + return ( + + - -

Page content

-
-
- ); - } +

Page content

+ +
+ ); } ``` @@ -207,114 +205,108 @@ class ProviderLinkExample extends React.Component { With a `theme`, the app provider component will set light, dark, and text colors for the [top bar](https://polaris.shopify.com/components/structure/top-bar) component when given a `background` color, as well as a logo for the top bar and [contextual save bar](https://polaris.shopify.com/components/forms/contextual-save-bar) components. ```jsx -class ProviderThemeExample extends React.Component { - state = { - isDirty: false, - searchFieldValue: '', - }; - - render() { - const {isDirty, searchFieldValue} = this.state; - - const theme = { - colors: { - topBar: { - background: '#357997', - }, - }, - logo: { - width: 124, - topBarSource: - 'https://cdn.shopify.com/s/files/1/0446/6937/files/jaded-pixel-logo-color.svg?6215648040070010999', - url: 'http://jadedpixel.com', - accessibilityLabel: 'Jaded Pixel', - contextualSaveBarSource: - 'https://cdn.shopify.com/s/files/1/0446/6937/files/jaded-pixel-logo-gray.svg?6215648040070010999', +function AppProviderThemeExample() { + const [isDirty, setIsDirty] = useState(false); + const [searchFieldValue, setSearchFieldValue] = useState(''); + + const handleSearchChange = useCallback( + (searchFieldValue) => setSearchFieldValue(searchFieldValue), + [], + ); + + const toggleIsDirty = useCallback( + () => setIsDirty((isDirty) => !isDirty), + [], + ); + + const theme = { + colors: { + topBar: { + background: '#357997', }, - }; - - const searchFieldMarkup = ( - - ); - - const topBarMarkup = ; - - const contentStatus = isDirty ? 'Disable' : 'Enable'; - const textStatus = isDirty ? 'enabled' : 'disabled'; - - const pageMarkup = ( - - - - - This setting is{' '} - {textStatus}. - - - - - ); + }, + logo: { + width: 124, + topBarSource: + 'https://cdn.shopify.com/s/files/1/0446/6937/files/jaded-pixel-logo-color.svg?6215648040070010999', + url: 'http://jadedpixel.com', + accessibilityLabel: 'Jaded Pixel', + contextualSaveBarSource: + 'https://cdn.shopify.com/s/files/1/0446/6937/files/jaded-pixel-logo-gray.svg?6215648040070010999', + }, + }; - const contextualSaveBarMarkup = isDirty ? ( - - ) : null; + const searchFieldMarkup = ( + + ); + + const topBarMarkup = ; + + const contentStatus = isDirty ? 'Disable' : 'Enable'; + const textStatus = isDirty ? 'enabled' : 'disabled'; + + const pageMarkup = ( + + + + + This setting is{' '} + {textStatus}. + + + + + ); + + const contextualSaveBarMarkup = isDirty ? ( + + ) : null; - return ( -
- + - - {contextualSaveBarMarkup} - {pageMarkup} - - -
- ); - } - - handleSearchChange = (searchFieldValue) => { - this.setState({searchFieldValue}); - }; - - toggleState = (key) => { - return () => { - this.setState((prevState) => ({[key]: !prevState[key]})); - }; - }; + }, + }} + > + + {contextualSaveBarMarkup} + {pageMarkup} + + + + ); } ``` @@ -323,118 +315,112 @@ class ProviderThemeExample extends React.Component { Provide specific keys and corresponding colors to the [top bar](https://polaris.shopify.com/components/structure/top-bar) component theme for finer control. When giving more than just the `background`, providing all keys is necessary to prevent falling back to default colors. ```jsx -class ProviderThemeExample extends React.Component { - state = { - isDirty: false, - searchFieldValue: '', - }; - - render() { - const {isDirty, searchFieldValue} = this.state; - - const theme = { - colors: { - topBar: { - background: '#357997', - backgroundLighter: '#6192a9', - color: '#FFFFFF', - }, +function AppProviderWithAllThemeKeysExample() { + const [isDirty, setIsDirty] = useState(false); + const [searchFieldValue, setSearchFieldValue] = useState(''); + + const handleSearchChange = useCallback( + (searchFieldValue) => setSearchFieldValue(searchFieldValue), + [], + ); + + const toggleIsDirty = useCallback( + () => setIsDirty((isDirty) => !isDirty), + [], + ); + + const theme = { + colors: { + topBar: { + background: '#357997', + backgroundLighter: '#6192a9', + color: '#FFFFFF', }, - logo: { - width: 124, - topBarSource: - 'https://cdn.shopify.com/s/files/1/0446/6937/files/jaded-pixel-logo-color.svg?6215648040070010999', - url: 'http://jadedpixel.com', - accessibilityLabel: 'Jaded Pixel', - contextualSaveBarSource: - 'https://cdn.shopify.com/s/files/1/0446/6937/files/jaded-pixel-logo-gray.svg?6215648040070010999', - }, - }; - - const searchFieldMarkup = ( - - ); - - const topBarMarkup = ; - - const contentStatus = isDirty ? 'Disable' : 'Enable'; - const textStatus = isDirty ? 'enabled' : 'disabled'; - - const pageMarkup = ( - - - - - This setting is{' '} - {textStatus}. - - - - - ); + }, + logo: { + width: 124, + topBarSource: + 'https://cdn.shopify.com/s/files/1/0446/6937/files/jaded-pixel-logo-color.svg?6215648040070010999', + url: 'http://jadedpixel.com', + accessibilityLabel: 'Jaded Pixel', + contextualSaveBarSource: + 'https://cdn.shopify.com/s/files/1/0446/6937/files/jaded-pixel-logo-gray.svg?6215648040070010999', + }, + }; - const contextualSaveBarMarkup = isDirty ? ( - - ) : null; + const searchFieldMarkup = ( + + ); + + const topBarMarkup = ; + + const contentStatus = isDirty ? 'Disable' : 'Enable'; + const textStatus = isDirty ? 'enabled' : 'disabled'; + + const pageMarkup = ( + + + + + This setting is{' '} + {textStatus}. + + + + + ); + + const contextualSaveBarMarkup = isDirty ? ( + + ) : null; - return ( -
- + - - {contextualSaveBarMarkup} - {pageMarkup} - - -
- ); - } - - handleSearchChange = (searchFieldValue) => { - this.setState({searchFieldValue}); - }; - - toggleState = (key) => { - return () => { - this.setState((prevState) => ({[key]: !prevState[key]})); - }; - }; + }, + }} + > + + {contextualSaveBarMarkup} + {pageMarkup} + + + + ); } ``` @@ -488,14 +474,14 @@ To provide access to your initialized Shopify App Bridge instance, we make it av ```js import React from 'react'; import {render} from 'react-dom'; -import {AppProvider, AppBridgeContext} from '@shopify/polaris'; +import {AppProvider, _SECRET_INTERNAL_APP_BRIDGE_CONTEXT} from '@shopify/polaris'; import {Redirect} from '@shopify/app-bridge/actions'; -class MyApp extends React.Component { - static contextType = AppBridgeContext; +function MyApp() { + const appBridge = useContext(_SECRET_INTERNAL_APP_BRIDGE_CONTEXT) - componentDidMount() { - const redirect = Redirect.create(this.context); + redirectToSettings() { + const redirect = Redirect.create(appBridge); // Go to {appOrigin}/settings redirect.dispatch(Redirect.Action.APP, '/settings');