diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Badge/examples/basic-badge.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Badge/examples/basic-badge.example.tsx new file mode 100644 index 0000000000..7b6e57335e --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Badge/examples/basic-badge.example.tsx @@ -0,0 +1,13 @@ +import { + reactExtension, + Badge, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return Available; +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Badge/examples/status-badge.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Badge/examples/status-badge.example.tsx new file mode 100644 index 0000000000..8bcfc7096b --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Badge/examples/status-badge.example.tsx @@ -0,0 +1,38 @@ +import { + reactExtension, + Badge, + BlockStack, + Text, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + + + Subscription + + Mini garden seeds + + + + $35.00 monthly + + Paused + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Banner/examples/basic-banner.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Banner/examples/basic-banner.example.tsx new file mode 100644 index 0000000000..a5795ddc9b --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Banner/examples/basic-banner.example.tsx @@ -0,0 +1,18 @@ +import { + reactExtension, + Banner, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/BlockLayout/examples/basic-blocklayout.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/BlockLayout/examples/basic-blocklayout.example.tsx new file mode 100644 index 0000000000..52311fb627 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/BlockLayout/examples/basic-blocklayout.example.tsx @@ -0,0 +1,23 @@ +import { + reactExtension, + BlockLayout, + View, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + + 60 + + + fill + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/BlockSpacer/examples/basic-blockspacer.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/BlockSpacer/examples/basic-blockspacer.example.tsx new file mode 100644 index 0000000000..f9b8e49684 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/BlockSpacer/examples/basic-blockspacer.example.tsx @@ -0,0 +1,24 @@ +import { + reactExtension, + BlockSpacer, + View, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + <> + + View + + + + View + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/BlockStack/examples/basic-blockstack.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/BlockStack/examples/basic-blockstack.example.tsx new file mode 100644 index 0000000000..51f41f9866 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/BlockStack/examples/basic-blockstack.example.tsx @@ -0,0 +1,26 @@ +import { + reactExtension, + BlockStack, + View, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + + View + + + View + + + View + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Button/examples/basic-button.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Button/examples/basic-button.example.tsx new file mode 100644 index 0000000000..0a8658efac --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Button/examples/basic-button.example.tsx @@ -0,0 +1,21 @@ +import { + reactExtension, + Button, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Chat/examples/basic-chat.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Chat/examples/basic-chat.example.tsx new file mode 100644 index 0000000000..b405b085a8 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Chat/examples/basic-chat.example.tsx @@ -0,0 +1,16 @@ +import { + reactExtension, + Chat, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +// This component requires the configuration of the `extensions.targeting.preloads.chat` in the extensions configuration file. +// Its value will be used as the `src` attribute of the Chat component. + +function Extension() { + return ; +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Checkbox/examples/basic-checkbox.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Checkbox/examples/basic-checkbox.example.tsx new file mode 100644 index 0000000000..7f1b2c46b8 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Checkbox/examples/basic-checkbox.example.tsx @@ -0,0 +1,17 @@ +import { + reactExtension, + Checkbox, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + Save this information for next time + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Choice/examples/basic-choice.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Choice/examples/basic-choice.example.tsx new file mode 100644 index 0000000000..be325cace0 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Choice/examples/basic-choice.example.tsx @@ -0,0 +1,43 @@ +import { + reactExtension, + Choice, + ChoiceList, + InlineStack, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + { + console.log( + `onChange event with value: ${value}`, + ); + }} + > + Ship + + + { + console.log( + `onChange event with value: ${value}`, + ); + }} + > + + Gift message + + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/ChoiceList/examples/basic-choicelist.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/ChoiceList/examples/basic-choicelist.example.tsx new file mode 100644 index 0000000000..85cb557cc6 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/ChoiceList/examples/basic-choicelist.example.tsx @@ -0,0 +1,76 @@ +import { + reactExtension, + BlockStack, + Choice, + ChoiceList, + Icon, + InlineStack, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + { + console.log( + `onChange event with value: ${value}`, + ); + }} + > + + } + id="ship" + > + Ship + + + } + id="ship-to-pickup-point" + > + Ship to pickup point + + + } + id="pick-up" + > + Pick up in store + + + { + console.log( + `onChange event with value: ${value}`, + ); + }} + > + + + Save this information for next time + + + Email me with news and offers + + + Text me with news and offers + + + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/ClipboardItem/examples/basic-clipboarditem.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/ClipboardItem/examples/basic-clipboarditem.example.tsx new file mode 100644 index 0000000000..84621ed91f --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/ClipboardItem/examples/basic-clipboarditem.example.tsx @@ -0,0 +1,27 @@ +import { + reactExtension, + Button, + ClipboardItem, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + <> + + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/ConsentCheckbox/examples/basic-consent-checkbox.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/ConsentCheckbox/examples/basic-consent-checkbox.example.tsx new file mode 100644 index 0000000000..a5796b3766 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/ConsentCheckbox/examples/basic-consent-checkbox.example.tsx @@ -0,0 +1,17 @@ +import { + reactExtension, + ConsentCheckbox, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + Text me with news and offers + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/ConsentCheckbox/examples/with-consent-phone-field-consent-checkbox.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/ConsentCheckbox/examples/with-consent-phone-field-consent-checkbox.example.tsx new file mode 100644 index 0000000000..21f8a5069c --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/ConsentCheckbox/examples/with-consent-phone-field-consent-checkbox.example.tsx @@ -0,0 +1,38 @@ +import { + reactExtension, + BlockStack, + ConsentCheckbox, + ConsentPhoneField, + InlineStack, + InlineSpacer, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + + Text me with news and offers + + + + + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/ConsentPhoneField/examples/basic-consent-phone-field.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/ConsentPhoneField/examples/basic-consent-phone-field.example.tsx new file mode 100644 index 0000000000..eeed9a4eb2 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/ConsentPhoneField/examples/basic-consent-phone-field.example.tsx @@ -0,0 +1,18 @@ +import { + reactExtension, + ConsentPhoneField, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/ConsentPhoneField/examples/with-consent-checkbox-consent-phone-field.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/ConsentPhoneField/examples/with-consent-checkbox-consent-phone-field.example.tsx new file mode 100644 index 0000000000..21f8a5069c --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/ConsentPhoneField/examples/with-consent-checkbox-consent-phone-field.example.tsx @@ -0,0 +1,38 @@ +import { + reactExtension, + BlockStack, + ConsentCheckbox, + ConsentPhoneField, + InlineStack, + InlineSpacer, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + + Text me with news and offers + + + + + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/DateField/examples/basic-datefield.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/DateField/examples/basic-datefield.example.tsx new file mode 100644 index 0000000000..4ac09fb934 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/DateField/examples/basic-datefield.example.tsx @@ -0,0 +1,13 @@ +import { + reactExtension, + DateField, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ; +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/DatePicker/examples/basic-datepicker.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/DatePicker/examples/basic-datepicker.example.tsx new file mode 100644 index 0000000000..dad6a66cff --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/DatePicker/examples/basic-datepicker.example.tsx @@ -0,0 +1,13 @@ +import { + reactExtension, + DatePicker, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ; +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Disclosure/examples/basic-disclosure.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Disclosure/examples/basic-disclosure.example.tsx new file mode 100644 index 0000000000..6f341fcfa5 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Disclosure/examples/basic-disclosure.example.tsx @@ -0,0 +1,20 @@ +import { + reactExtension, + Disclosure, + Button, + View, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + + Content + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Divider/examples/basic-divider.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Divider/examples/basic-divider.example.tsx new file mode 100644 index 0000000000..d530a1bed2 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Divider/examples/basic-divider.example.tsx @@ -0,0 +1,13 @@ +import { + reactExtension, + Divider, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ; +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/DropZone/examples/basic-DropZone-react.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/DropZone/examples/basic-DropZone-react.example.tsx index 3870c2ff7e..e781bfb2d2 100644 --- a/packages/ui-extensions-react/src/surfaces/customer-account/components/DropZone/examples/basic-DropZone-react.example.tsx +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/DropZone/examples/basic-DropZone-react.example.tsx @@ -1,14 +1,13 @@ import { - DropZone, reactExtension, + DropZone, } from '@shopify/ui-extensions-react/customer-account'; -import React from 'react'; export default reactExtension( 'customer-account.page.render', - () => , + () => , ); -function App() { +function Extension() { return ; } diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Form/examples/basic-form.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Form/examples/basic-form.example.tsx new file mode 100644 index 0000000000..fd0e667d3f --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Form/examples/basic-form.example.tsx @@ -0,0 +1,44 @@ +import { + reactExtension, + BlockSpacer, + Button, + Form, + Grid, + GridItem, + TextField, + View, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( +
+ console.log('onSubmit event') + } + > + + + + + + + + + + + + + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Grid/examples/basic-grid.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Grid/examples/basic-grid.example.tsx new file mode 100644 index 0000000000..9a28f21de5 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Grid/examples/basic-grid.example.tsx @@ -0,0 +1,39 @@ +import { + reactExtension, + Grid, + View, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + + 20% / 300 + + + fill / 300 + + + auto / 300 + + + 20% / auto + + + fill / auto + + + auto / auto + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/GridItem/examples/basic-griditem.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/GridItem/examples/basic-griditem.example.tsx new file mode 100644 index 0000000000..0f8f5762e2 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/GridItem/examples/basic-griditem.example.tsx @@ -0,0 +1,39 @@ +import { + reactExtension, + Grid, + GridItem, + View, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + + 20% / 300 + + + fill / 300 + + + auto / 300 + + + + 20% + fill / auto + + + + auto / auto + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Heading/examples/basic-heading.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Heading/examples/basic-heading.example.tsx new file mode 100644 index 0000000000..302fe93755 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Heading/examples/basic-heading.example.tsx @@ -0,0 +1,13 @@ +import { + reactExtension, + Heading, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return Store name; +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/HeadingGroup/examples/basic-headinggroup.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/HeadingGroup/examples/basic-headinggroup.example.tsx new file mode 100644 index 0000000000..a79f759a51 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/HeadingGroup/examples/basic-headinggroup.example.tsx @@ -0,0 +1,26 @@ +import { + reactExtension, + HeadingGroup, + Heading, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + <> + Heading <h1> + + + Heading <h2> + + + Heading <h3> + + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Icon/examples/basic-icon.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Icon/examples/basic-icon.example.tsx new file mode 100644 index 0000000000..68685c5f79 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Icon/examples/basic-icon.example.tsx @@ -0,0 +1,13 @@ +import { + reactExtension, + Icon, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ; +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Image/examples/basic-image.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Image/examples/basic-image.example.tsx new file mode 100644 index 0000000000..12798cb2f1 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Image/examples/basic-image.example.tsx @@ -0,0 +1,15 @@ +import { + reactExtension, + Image, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/InlineLayout/examples/basic-inlinelayout.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/InlineLayout/examples/basic-inlinelayout.example.tsx new file mode 100644 index 0000000000..c3dcda016b --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/InlineLayout/examples/basic-inlinelayout.example.tsx @@ -0,0 +1,23 @@ +import { + reactExtension, + InlineLayout, + View, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + + 20% + + + fill + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/InlineSpacer/examples/basic-inlinespacer.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/InlineSpacer/examples/basic-inlinespacer.example.tsx new file mode 100644 index 0000000000..84e54dd4e0 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/InlineSpacer/examples/basic-inlinespacer.example.tsx @@ -0,0 +1,33 @@ +import { + reactExtension, + InlineSpacer, + InlineStack, + View, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + + View + + + + View + + + + View + + + + View + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/InlineStack/examples/basic-inlinestack.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/InlineStack/examples/basic-inlinestack.example.tsx new file mode 100644 index 0000000000..702a053d75 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/InlineStack/examples/basic-inlinestack.example.tsx @@ -0,0 +1,29 @@ +import { + reactExtension, + InlineStack, + View, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + + View + + + View + + + View + + + View + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Link/examples/basic-link.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Link/examples/basic-link.example.tsx new file mode 100644 index 0000000000..3e2504ce52 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Link/examples/basic-link.example.tsx @@ -0,0 +1,17 @@ +import { + reactExtension, + Link, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + Sustainability fund + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/List/examples/basic-list.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/List/examples/basic-list.example.tsx new file mode 100644 index 0000000000..f6a3681fbf --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/List/examples/basic-list.example.tsx @@ -0,0 +1,20 @@ +import { + reactExtension, + List, + ListItem, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + 100% organic cotton + Made in Canada + Machine washable + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/ListItem/examples/basic-listitem.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/ListItem/examples/basic-listitem.example.tsx new file mode 100644 index 0000000000..56e18f61f6 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/ListItem/examples/basic-listitem.example.tsx @@ -0,0 +1,18 @@ +import { + reactExtension, + List, + ListItem, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + 100% organic cotton + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Map/examples/basic-map.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Map/examples/basic-map.example.tsx new file mode 100644 index 0000000000..e7bb2cc93d --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Map/examples/basic-map.example.tsx @@ -0,0 +1,21 @@ +import { + reactExtension, + Map, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/MapMarker/examples/basic-mapmarker.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/MapMarker/examples/basic-mapmarker.example.tsx new file mode 100644 index 0000000000..55943f749f --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/MapMarker/examples/basic-mapmarker.example.tsx @@ -0,0 +1,28 @@ +import { + reactExtension, + Map, + MapMarker, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/MapPopover/examples/basic-mappopover.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/MapPopover/examples/basic-mappopover.example.tsx new file mode 100644 index 0000000000..27562f8f85 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/MapPopover/examples/basic-mappopover.example.tsx @@ -0,0 +1,34 @@ +import { + reactExtension, + Map, + MapMarker, + MapPopover, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + + Blue Mountains National Park store + + } + /> + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Modal/examples/basic-modal.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Modal/examples/basic-modal.example.tsx new file mode 100644 index 0000000000..06cdbf0148 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Modal/examples/basic-modal.example.tsx @@ -0,0 +1,52 @@ +import { + reactExtension, + useApi, + Button, + Link, + Modal, + TextBlock, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + const {ui} = useApi(); + + return ( + + + We have a 30-day return policy, which + means you have 30 days after receiving + your item to request a return. + + + To be eligible for a return, your item + must be in the same condition that you + received it, unworn or unused, with + tags, and in its original packaging. + You’ll also need the receipt or proof + of purchase. + + + + } + > + Return policy + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/PaymentIcon/examples/basic-paymenticon.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/PaymentIcon/examples/basic-paymenticon.example.tsx new file mode 100644 index 0000000000..84f3a83815 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/PaymentIcon/examples/basic-paymenticon.example.tsx @@ -0,0 +1,13 @@ +import { + reactExtension, + PaymentIcon, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ; +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/PhoneField/examples/basic-phonefield.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/PhoneField/examples/basic-phonefield.example.tsx new file mode 100644 index 0000000000..091603bc7b --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/PhoneField/examples/basic-phonefield.example.tsx @@ -0,0 +1,18 @@ +import { + reactExtension, + PhoneField, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Popover/examples/basic-popover.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Popover/examples/basic-popover.example.tsx new file mode 100644 index 0000000000..1f77beca94 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Popover/examples/basic-popover.example.tsx @@ -0,0 +1,41 @@ +import { + reactExtension, + Pressable, + Popover, + View, + TextBlock, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + + + A thoughtful way to pay + + Tap don’t type + + Shop Pay remembers your important + details, so you can fill carts, not + forms. And everything is encrypted + so you can speed safely through + checkout. + + + + } + > + More info + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Pressable/examples/basic-pressable.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Pressable/examples/basic-pressable.example.tsx new file mode 100644 index 0000000000..0f4ea8b827 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Pressable/examples/basic-pressable.example.tsx @@ -0,0 +1,27 @@ +import { + reactExtension, + Icon, + InlineLayout, + Pressable, + Text, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + + Details + + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/ProductThumbnail/examples/basic-productthumbnail.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/ProductThumbnail/examples/basic-productthumbnail.example.tsx new file mode 100644 index 0000000000..76e714261c --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/ProductThumbnail/examples/basic-productthumbnail.example.tsx @@ -0,0 +1,18 @@ +import { + reactExtension, + ProductThumbnail, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Progress/examples/basic-progress.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Progress/examples/basic-progress.example.tsx new file mode 100644 index 0000000000..1f70769795 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Progress/examples/basic-progress.example.tsx @@ -0,0 +1,15 @@ +import { + reactExtension, + Progress, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/QRCode/examples/basic-qrcode.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/QRCode/examples/basic-qrcode.example.tsx new file mode 100644 index 0000000000..8ffc4ca350 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/QRCode/examples/basic-qrcode.example.tsx @@ -0,0 +1,26 @@ +import { + reactExtension, + Link, + QRCode, + TextBlock, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + <> + + + + Scan to visit{' '} + + Shopify.com + + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/ScrollView/examples/basic-scrollview.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/ScrollView/examples/basic-scrollview.example.tsx new file mode 100644 index 0000000000..4b4387f822 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/ScrollView/examples/basic-scrollview.example.tsx @@ -0,0 +1,52 @@ +import { + reactExtension, + ScrollView, + View, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + + View + + + View + + + View + + + View + + + View + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Select/examples/basic-select.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Select/examples/basic-select.example.tsx new file mode 100644 index 0000000000..f6e97cfcaa --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Select/examples/basic-select.example.tsx @@ -0,0 +1,44 @@ +import { + reactExtension, + Select, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Sheet/examples/basic-sheet.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Sheet/examples/basic-sheet.example.tsx new file mode 100644 index 0000000000..f7b6414b9e --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Sheet/examples/basic-sheet.example.tsx @@ -0,0 +1,33 @@ +import { + reactExtension, + Link, + Sheet, + TextBlock, +} from '@shopify/ui-extensions-react/customer-account'; + +// This component requires access to Customer Privacy API to be rendered. + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + + Basic Sheet Content + + + } + > + Open sheet + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/SkeletonImage/examples/basic-skeletonimage.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/SkeletonImage/examples/basic-skeletonimage.example.tsx new file mode 100644 index 0000000000..5734f9e61c --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/SkeletonImage/examples/basic-skeletonimage.example.tsx @@ -0,0 +1,18 @@ +import { + reactExtension, + SkeletonImage, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/SkeletonText/examples/basic-skeletontext.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/SkeletonText/examples/basic-skeletontext.example.tsx new file mode 100644 index 0000000000..d1a738aef7 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/SkeletonText/examples/basic-skeletontext.example.tsx @@ -0,0 +1,13 @@ +import { + reactExtension, + SkeletonText, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ; +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/SkeletonTextBlock/examples/basic-skeletontextblock.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/SkeletonTextBlock/examples/basic-skeletontextblock.example.tsx new file mode 100644 index 0000000000..51ed9625c2 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/SkeletonTextBlock/examples/basic-skeletontextblock.example.tsx @@ -0,0 +1,13 @@ +import { + reactExtension, + SkeletonTextBlock, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ; +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Spinner/examples/basic-spinner.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Spinner/examples/basic-spinner.example.tsx new file mode 100644 index 0000000000..e373970af7 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Spinner/examples/basic-spinner.example.tsx @@ -0,0 +1,13 @@ +import { + reactExtension, + Spinner, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ; +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Stepper/examples/basic-stepper.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Stepper/examples/basic-stepper.example.tsx new file mode 100644 index 0000000000..51b42d446b --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Stepper/examples/basic-stepper.example.tsx @@ -0,0 +1,13 @@ +import { + reactExtension, + Stepper, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ; +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Switch/examples/basic-switch.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Switch/examples/basic-switch.example.tsx new file mode 100644 index 0000000000..26218780fb --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Switch/examples/basic-switch.example.tsx @@ -0,0 +1,15 @@ +import { + reactExtension, + Switch, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Tag/examples/basic-tag.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Tag/examples/basic-tag.example.tsx new file mode 100644 index 0000000000..d827ac139b --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Tag/examples/basic-tag.example.tsx @@ -0,0 +1,13 @@ +import { + reactExtension, + Tag, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return SPRING; +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Text/examples/basic-text.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Text/examples/basic-text.example.tsx new file mode 100644 index 0000000000..493039f444 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Text/examples/basic-text.example.tsx @@ -0,0 +1,23 @@ +import { + reactExtension, + Text, + BlockStack, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + Total + Total + Total + Total + Total + Total + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/TextBlock/examples/basic-textblock.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/TextBlock/examples/basic-textblock.example.tsx new file mode 100644 index 0000000000..5d55316e2d --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/TextBlock/examples/basic-textblock.example.tsx @@ -0,0 +1,29 @@ +import { + reactExtension, + TextBlock, + BlockStack, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + + We have a 30-day return policy, which + means you have 30 days after receiving + your item to request a return. + + + To be eligible for a return, your item + must be in the same condition that you + received it, unworn or unused, with tags, + and in its original packaging. You’ll also + need the receipt or proof of purchase. + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/TextField/examples/basic-textfield.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/TextField/examples/basic-textfield.example.tsx new file mode 100644 index 0000000000..c425e76e02 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/TextField/examples/basic-textfield.example.tsx @@ -0,0 +1,13 @@ +import { + reactExtension, + TextField, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ; +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/ToggleButton/examples/basic-togglebutton.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/ToggleButton/examples/basic-togglebutton.example.tsx new file mode 100644 index 0000000000..9246000cd3 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/ToggleButton/examples/basic-togglebutton.example.tsx @@ -0,0 +1,25 @@ +import { + reactExtension, + ToggleButton, + ToggleButtonGroup, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + { + console.log( + `onChange event with value: ${value}`, + ); + }} + > + None + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/ToggleButtonGroup/examples/basic-togglebuttongroup.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/ToggleButtonGroup/examples/basic-togglebuttongroup.example.tsx new file mode 100644 index 0000000000..e0d0513857 --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/ToggleButtonGroup/examples/basic-togglebuttongroup.example.tsx @@ -0,0 +1,72 @@ +import { + reactExtension, + BlockStack, + InlineLayout, + Text, + ToggleButton, + ToggleButtonGroup, + View, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + { + console.log( + `onChange event with value: ${value}`, + ); + }} + > + + + + None + + + + + 100 + + points + + + + + + 200 + + points + + + + + + 300 + + points + + + + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/Tooltip/examples/basic-tooltip.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/Tooltip/examples/basic-tooltip.example.tsx new file mode 100644 index 0000000000..22de26c56e --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/Tooltip/examples/basic-tooltip.example.tsx @@ -0,0 +1,26 @@ +import { + reactExtension, + Icon, + Pressable, + Tooltip, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + In case we need to contact you about + your order + + } + > + + + ); +} diff --git a/packages/ui-extensions-react/src/surfaces/customer-account/components/View/examples/basic-view.example.tsx b/packages/ui-extensions-react/src/surfaces/customer-account/components/View/examples/basic-view.example.tsx new file mode 100644 index 0000000000..5965b263db --- /dev/null +++ b/packages/ui-extensions-react/src/surfaces/customer-account/components/View/examples/basic-view.example.tsx @@ -0,0 +1,17 @@ +import { + reactExtension, + View, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension( + 'customer-account.page.render', + () => , +); + +function Extension() { + return ( + + View + + ); +} diff --git a/packages/ui-extensions/docs/surfaces/build-doc-shared.mjs b/packages/ui-extensions/docs/surfaces/build-doc-shared.mjs new file mode 100644 index 0000000000..4d8e594b7f --- /dev/null +++ b/packages/ui-extensions/docs/surfaces/build-doc-shared.mjs @@ -0,0 +1,72 @@ +/* eslint-disable no-undef, no-console */ +import childProcess from 'child_process'; +import fs from 'fs/promises'; +import {existsSync} from 'fs'; +import path from 'path'; + +export const replaceFileContent = async ({ + filePaths, + searchValue, + replaceValue, +}) => { + const files = Array.isArray(filePaths) ? filePaths : [filePaths]; + for (const filePath of files) { + const content = await fs.readFile(filePath, 'utf8'); + // @ts-ignore -- TS should know this is a string but it doesn't + const replacedContent = content.replaceAll(searchValue, replaceValue); + await fs.writeFile(filePath, replacedContent); + } +}; + +export const generateFiles = async ({ + scripts, + outputDir, + rootPath, + generatedDocsDataFile, + generatedStaticPagesFile, + transformJson, +}) => { + scripts.forEach((script) => childProcess.execSync(script, {stdio: 'pipe'})); + + const srcFiles = await fs.readdir(rootPath, {recursive: true}); + const builtFiles = srcFiles.filter((file) => file.endsWith('.ts')); + await Promise.all( + builtFiles.map((file) => { + const jsFilePath = path.join(rootPath, file.replace('.ts', '.js')); + return existsSync(jsFilePath) ? fs.rm(jsFilePath) : Promise.resolve(); + }), + ); + + const generatedFiles = [path.join(outputDir, generatedDocsDataFile)]; + if (generatedStaticPagesFile) { + generatedFiles.push(path.join(outputDir, generatedStaticPagesFile)); + } + + // Make sure https://shopify.dev URLs are relative so they work in Spin. + // See https://github.com/Shopify/generate-docs/issues/181 + await replaceFileContent({ + filePaths: generatedFiles, + searchValue: 'https://shopify.dev', + replaceValue: '', + }); + + if (transformJson) { + await transformJson(path.join(outputDir, generatedDocsDataFile)); + } +}; + +export const copyGeneratedToShopifyDev = async ({ + generatedDocsPath, + shopifyDevPath, + shopifyDevDBPath, +}) => { + const shopifyDevExists = existsSync(shopifyDevPath); + if (!shopifyDevExists) { + console.log( + `Not copying docs to shopify-dev because it was not found at ${shopifyDevPath}.`, + ); + process.exit(); + } + + await fs.cp(generatedDocsPath, shopifyDevDBPath, {recursive: true}); +}; diff --git a/packages/ui-extensions/docs/surfaces/customer-account/build-docs.mjs b/packages/ui-extensions/docs/surfaces/customer-account/build-docs.mjs new file mode 100644 index 0000000000..5825f71acb --- /dev/null +++ b/packages/ui-extensions/docs/surfaces/customer-account/build-docs.mjs @@ -0,0 +1,288 @@ +/* eslint-disable no-undef, no-console */ +import {exec as execCb, execSync} from 'child_process'; +import fs from 'fs/promises'; +import {existsSync} from 'fs'; +import path from 'path'; +import {fileURLToPath} from 'url'; +import {promisify} from 'util'; + +import { + copyGeneratedToShopifyDev, + replaceFileContent, +} from '../build-doc-shared.mjs'; + +const execAsync = promisify(execCb); + +const EXTENSIONS_API_VERSION = process.argv[2] || 'unstable'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const rootPath = path.join(__dirname, '../../..'); +const docsRelativePath = 'docs/surfaces/customer-account'; +const docsGeneratedRelativePath = 'docs/surfaces/customer-account/generated'; +const srcRelativePath = 'src/surfaces/customer-account'; +const reactSrcRelativePath = `../ui-extensions-react/${srcRelativePath}`; +const checkoutSrcRelativePath = 'src/surfaces/checkout'; +const checkoutComponentsRelativePath = `${checkoutSrcRelativePath}/components`; +const docsPath = path.join(rootPath, docsRelativePath); +const srcPath = path.join(rootPath, srcRelativePath); +const checkoutSrcPath = path.join(rootPath, checkoutSrcRelativePath); +const checkoutComponentsDir = path.join(checkoutSrcPath, 'components'); +const generatedDocsPath = path.join(docsPath, 'generated'); +const shopifyDevPath = path.join(rootPath, '../../../shopify-dev'); +const shopifyDevDBPath = path.join( + shopifyDevPath, + 'areas/platforms/shopify-dev/db/data/docs/templated_apis', +); + +const generatedDocsDataFile = 'generated_docs_data.json'; +const generatedStaticPagesFile = 'generated_static_pages.json'; + +const componentDefs = path.join(srcPath, 'components.d.ts'); +const tempComponentDefs = path.join(srcPath, 'components.ts'); + +const tsconfig = 'tsconfig.docs.json'; + +const maxBuffer = 50 * 1024 * 1024; + +const copyCheckoutTypesToTemp = async () => { + const files = await fs.readdir(checkoutComponentsDir); + return Promise.all( + files + .filter( + (file) => file.endsWith('.d.ts') && file !== 'components-shared.d.ts', + ) + .map(async (file) => { + const srcFile = path.join(checkoutComponentsDir, file); + const tempFile = path.join( + checkoutComponentsDir, + file.replace('.d.ts', '.ts'), + ); + await fs.copyFile(srcFile, tempFile); + return tempFile; + }), + ); +}; + +const cleanupTempFiles = async (tempFiles) => { + await Promise.all( + tempFiles + .filter((file) => existsSync(file)) + .map((file) => fs.rm(file)), + ); +}; + +const cleanupGeneratedJsFiles = async (directories) => { + await Promise.all( + directories.map(async (dir) => { + if (!existsSync(dir)) return; + const files = await fs.readdir(dir, {recursive: true}); + await Promise.all( + files + .filter((file) => file.endsWith('.js')) + .map((file) => { + const jsPath = path.join(dir, file); + const tsPath = path.join(dir, file.replace(/\.js$/, '.ts')); + return existsSync(tsPath) ? fs.rm(jsPath) : Promise.resolve(); + }), + ); + }), + ); +}; + +const generateExtensionsDocs = async () => { + console.log( + `Building Customer Account UI Extensions docs for ${EXTENSIONS_API_VERSION} version`, + ); + + if (EXTENSIONS_API_VERSION === 'unstable') { + console.log( + "You can add a calver version argument (e.g. 'yarn docs:customer-account 2024-07') to generate the docs for a stable version.", + ); + } + + const outputDir = `${docsGeneratedRelativePath}/customer_account_ui_extensions/${EXTENSIONS_API_VERSION}`; + const tempRefOutputDir = `${outputDir}/_temp_ref`; + const tempCompOutputDir = `${outputDir}/_temp_comp`; + + await fs.mkdir(outputDir, {recursive: true}); + await fs.mkdir(tempRefOutputDir, {recursive: true}); + await fs.mkdir(tempCompOutputDir, {recursive: true}); + + // Single tsc step — tsconfig.docs.json already covers all .doc.ts files + // (reference, staticPages, categories, and component docs) + console.log('Compiling TypeScript...'); + execSync( + `yarn tsc --project ${docsRelativePath}/${tsconfig} --moduleResolution node --target esNext --module CommonJS`, + {stdio: 'pipe'}, + ); + + // Split generate-docs into independent parallel commands. + // Internally, generate-docs creates a TypeScript program for every + // --typesInput directory × every --input directory. Splitting reference + // docs from component docs avoids redundant type parsing: + // - Reference docs (APIs/targets) only need customer-account types + // - Component docs only need checkout component types + // (customer-account types are included automatically as the base path) + console.log('Generating docs in parallel...'); + const overridePath = `./${docsRelativePath}/typeOverride.json`; + await Promise.all([ + execAsync( + `yarn generate-docs --overridePath ${overridePath} --input ./${docsRelativePath}/reference --typesInput ./${srcRelativePath} ./${reactSrcRelativePath} --output ./${tempRefOutputDir}`, + {maxBuffer}, + ), + execAsync( + `yarn generate-docs --overridePath ${overridePath} --input ./${srcRelativePath} --typesInput ./${srcRelativePath} ./${checkoutSrcRelativePath} --output ./${tempCompOutputDir}`, + {maxBuffer}, + ), + execAsync( + `yarn generate-docs --isLandingPage --input ./${docsRelativePath}/staticPages --output ./${outputDir}`, + {maxBuffer}, + ), + ]); + + // Merge the two generated_docs_data.json files + const [refData, compData] = await Promise.all([ + fs + .readFile(path.join(tempRefOutputDir, generatedDocsDataFile), 'utf8') + .then(JSON.parse), + fs + .readFile(path.join(tempCompOutputDir, generatedDocsDataFile), 'utf8') + .then(JSON.parse), + ]); + const mergedData = [...refData, ...compData].filter(Boolean); + await fs.writeFile( + path.join(outputDir, generatedDocsDataFile), + JSON.stringify(mergedData, null, 2), + ); + + // Clean up temp directories + await Promise.all([ + fs.rm(tempRefOutputDir, {recursive: true}), + fs.rm(tempCompOutputDir, {recursive: true}), + ]); + + // Clean up .js files only in directories where tsc output lands + await cleanupGeneratedJsFiles([ + path.join(rootPath, docsRelativePath), + path.join(rootPath, srcRelativePath), + path.join(rootPath, 'src/docs/shared'), + ]); + + const generatedFiles = [path.join(outputDir, generatedDocsDataFile)]; + if (generatedStaticPagesFile) { + generatedFiles.push(path.join(outputDir, generatedStaticPagesFile)); + } + + // Make sure https://shopify.dev URLs are relative so they work in Spin + await replaceFileContent({ + filePaths: generatedFiles, + searchValue: 'https://shopify.dev', + replaceValue: '', + }); + + // Replace 'unstable' with the exact API version in relative doc links + await replaceFileContent({ + filePaths: path.join(outputDir, generatedDocsDataFile), + searchValue: '/docs/api//unstable/', + replaceValue: `/docs/api/customer-account-ui-extensions/${EXTENSIONS_API_VERSION}`, + }); + + await fs.cp( + path.join(docsPath, 'screenshots'), + path.join( + shopifyDevPath, + 'areas/platforms/shopify-dev/content/assets/images/templated-apis-screenshots/customer-account-ui-extensions', + EXTENSIONS_API_VERSION, + ), + {recursive: true}, + ); +}; + +const surfaceFiles = [ + path.join(rootPath, 'src/surfaces/checkout.ts'), + path.join(rootPath, 'src/surfaces/admin.ts'), + path.join(rootPath, 'src/surfaces/point-of-sale.ts'), + path.join(rootPath, '../ui-extensions-react/src/surfaces/checkout.ts'), + path.join(rootPath, '../ui-extensions-react/src/surfaces/admin.ts'), + path.join(rootPath, '../ui-extensions-react/src/surfaces/point-of-sale.ts'), +]; + +const blankSurfaces = async () => { + const originals = new Map(); + for (const file of surfaceFiles) { + if (existsSync(file)) { + originals.set(file, await fs.readFile(file, 'utf8')); + await fs.writeFile(file, 'export {}\n'); + } + } + return originals; +}; + +const restoreSurfaces = async (originals) => { + for (const [file, content] of originals) { + await fs.writeFile(file, content); + } +}; + +let checkoutTempFiles = []; +let surfaceOriginals = new Map(); +try { + if (existsSync(generatedDocsPath)) { + await fs.rm(generatedDocsPath, {recursive: true}); + } + if (existsSync(componentDefs)) { + await fs.copyFile(componentDefs, tempComponentDefs); + await replaceFileContent({ + filePaths: tempComponentDefs, + searchValue: /typeof globalThis\.HTMLElement/g, + replaceValue: 'any', + }); + } + + console.log('Blanking other surface files to avoid type conflicts...'); + surfaceOriginals = await blankSurfaces(); + + console.log('Copying checkout .d.ts files to temporary .ts files...'); + checkoutTempFiles = await copyCheckoutTypesToTemp(); + + await generateExtensionsDocs(); + + // Generate targets.json + console.log('Generating targets.json...'); + try { + execSync(`node ${path.join(docsPath, 'build-docs-targets-json.mjs')} ${EXTENSIONS_API_VERSION}`, { + stdio: 'inherit', + cwd: rootPath, + }); + console.log('✅ Generated targets.json'); + } catch (targetsError) { + console.warn( + 'Warning: Failed to generate targets.json:', + targetsError.message, + ); + } + + await copyGeneratedToShopifyDev({ + generatedDocsPath, + shopifyDevPath, + shopifyDevDBPath, + }); + + if (existsSync(tempComponentDefs)) { + await fs.rm(tempComponentDefs); + } + await cleanupTempFiles(checkoutTempFiles); + await restoreSurfaces(surfaceOriginals); +} catch (error) { + await cleanupTempFiles(checkoutTempFiles); + if (existsSync(tempComponentDefs)) { + await fs.rm(tempComponentDefs); + } + await restoreSurfaces(surfaceOriginals); + console.error(error); + console.log(error.stdout?.toString?.() ?? ''); + console.log(error.stderr?.toString?.() ?? ''); + process.exit(1); +} diff --git a/packages/ui-extensions/docs/surfaces/customer-account/categories/components.doc.ts b/packages/ui-extensions/docs/surfaces/customer-account/categories/components.doc.ts deleted file mode 100644 index b5494702d1..0000000000 --- a/packages/ui-extensions/docs/surfaces/customer-account/categories/components.doc.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {CategoryTemplateSchema} from '@shopify/generate-docs'; - -const data: CategoryTemplateSchema = { - category: 'components', - sections: [ - { - type: 'Generic', - anchorLink: 'additional-components', - title: 'Additional components', - sectionContent: - 'In addition to the components below, you can also use checkout components to build customer account UI extensions.', - sectionCard: [ - { - type: 'blocks', - name: 'Checkout components', - subtitle: 'More components', - url: '/docs/api/checkout-ui-extensions/components', - }, - ], - }, - ], -}; - -export default data; diff --git a/packages/ui-extensions/package.json b/packages/ui-extensions/package.json index 38a8b1c062..efc5c8a098 100644 --- a/packages/ui-extensions/package.json +++ b/packages/ui-extensions/package.json @@ -6,7 +6,8 @@ "gen-docs:admin": "bash ./docs/surfaces/admin/create-doc-files.sh \"admin\"", "docs:checkout": "bash ./docs/surfaces/checkout/build-docs.sh", "docs:point-of-sale": "bash ./docs/surfaces/point-of-sale/build-docs.sh", - "docs:customer-account": "bash ./docs/surfaces/customer-account/build-docs.sh" + "docs:point-of-sale:dev": "nodemon --watch src/surfaces/point-of-sale/ --watch docs/surfaces/point-of-sale/ -e ts,tsx,json,png,txt --ignore '**/generated/**' --exec 'yarn docs:point-of-sale'", + "docs:customer-account": "node ./docs/surfaces/customer-account/build-docs.mjs" }, "main": "index.js", "module": "index.mjs", diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Avatar/Avatar.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Avatar/Avatar.doc.ts index 6d1c0d7597..13ac136b44 100644 --- a/packages/ui-extensions/src/surfaces/customer-account/components/Avatar/Avatar.doc.ts +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Avatar/Avatar.doc.ts @@ -16,7 +16,8 @@ const data: ReferenceEntityTemplateSchema = { filePath: 'components/Avatar/Avatar.ts', }, ], - category: 'components', + category: 'UI components', + subCategory: 'Media and visuals', defaultExample: { image: 'avatar-preview.png', altText: diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Badge/Badge.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Badge/Badge.doc.ts new file mode 100644 index 0000000000..9a693ccae7 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Badge/Badge.doc.ts @@ -0,0 +1,76 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Badge', + description: + 'Use badges to highlight contextual information, like a label or a status, about an object. An object can be anything that has a status or label attributed to it, like an order or subscription.', + isVisualComponent: true, + thumbnail: 'badge-thumbnail.png', + requires: '', + type: '', + definitions: [ + { + title: 'BadgeProps', + description: '', + type: 'BadgeProps', + }, + ], + category: 'UI components', + subCategory: 'Feedback and status indicators', + + defaultExample: { + image: 'badge-default.png', + codeblock: { + title: 'Basic Badge', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Badge/examples/basic-badge.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-badge.example.ts', + language: 'js', + }, + ], + }, + }, + examples: { + description: '', + examples: [ + { + image: 'badge-status.png', + codeblock: { + title: 'Using the Badge component as a status indicator', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Badge/examples/status-badge.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/status-badge.example.ts', + language: 'js', + }, + ], + }, + }, + ], + }, + + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + + sectionContent: + '- Aim for one word per badge.\n\n- For complex states that require 2 words, use sentence case.\n\n- For status badge labels, use an adjective (for example, "Available" or "Complete") or a verb in the past tense (for example, "Delivered" or "Delayed")\n\n- Use the tone prop to indicate the level of importance. `subdued` provides the least emphasis, while `critical` indicates the highest level of urgency.\n\n- Write a useful `accessibilityLabel` so that customers using assistive technology can access the full meaning of the badge in context. For badges with the `critical` tone, include information that conveys the urgency of the badge (for example, "Warning" or "Alert").\n\n- A badge should always be attributed to an object on the page.', + }, + ], + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Badge/examples/basic-badge.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Badge/examples/basic-badge.example.ts new file mode 100644 index 0000000000..2086769f3d --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Badge/examples/basic-badge.example.ts @@ -0,0 +1,7 @@ +import {extension, Badge} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const badge = root.createComponent(Badge, undefined, 'Available'); + + root.appendChild(badge); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Badge/examples/status-badge.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Badge/examples/status-badge.example.ts new file mode 100644 index 0000000000..458ba3b0a9 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Badge/examples/status-badge.example.ts @@ -0,0 +1,39 @@ +import { + extension, + Badge, + BlockStack, + Text, +} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const container = root.createComponent( + BlockStack, + { + border: 'base', + padding: 'large200', + spacing: 'base', + borderRadius: 'large', + }, + [ + root.createComponent(BlockStack, {spacing: 'none'}, [ + root.createComponent( + Text, + {size: 'large', emphasis: 'bold'}, + 'Subscription', + ), + root.createComponent(Text, undefined, 'Mini garden seeds'), + ]), + + root.createComponent( + BlockStack, + {spacing: 'none', inlineAlignment: 'start'}, + [ + root.createComponent(Text, {emphasis: 'bold'}, '$35.00 monthly'), + root.createComponent(Badge, {tone: 'subdued'}, 'Paused'), + ], + ), + ], + ); + + root.appendChild(container); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Banner/Banner.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Banner/Banner.doc.ts new file mode 100644 index 0000000000..5706b9684b --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Banner/Banner.doc.ts @@ -0,0 +1,57 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Banner', + description: + 'Use banners to communicate important messages to customers in a prominent way.', + isVisualComponent: true, + thumbnail: 'banner-thumbnail.png', + requires: '', + type: '', + definitions: [ + { + title: 'BannerProps', + description: '', + type: 'BannerProps', + }, + ], + category: 'UI components', + subCategory: 'Feedback and status indicators', + defaultExample: { + image: 'banner-default.png', + codeblock: { + title: 'Basic Banner', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Banner/examples/basic-banner.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-banner.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + '- Use banners thoughtfully and sparingly, and only for the most important information. Too many banners distract customers from completing checkout.\n\n- Banners are typically displayed at the top of a page or a section, if they relate to specific content. Place banners below the relevant page or section header.\n\n- Include a Button component with next steps when possible.\n\n- Make banners dismissible, unless they contain critical information or an important step that customers need to take.\n\n- Use the `info` banner to update customers about a change or to give them advice.\n\n- Use the `warning` banner to display information that needs attention or that customers need to take action on. Warning banners can be stressful for customers, so be cautious about using them.\n\n- Use the `critical` banner to communicate problems that customers need to resolve immediately to complete checkout.', + }, + { + type: 'Generic', + anchorLink: 'status', + title: 'Status', + sectionContent: + '| Value | Description |\n| --- | --- |\n| "info" | Convey general information or actions that aren’t critical or tied to a particular action.. |\n| "success" | Use rarely, only if you need additional visual confirmation that a non-standard action has been completed successfully, for example adding an item to an order as an upsell. |\n| "warning" | Display information that needs attention or that customers should take action on. Seeing these banners can be stressful for customers so be cautious about using them. Should not block progress to next step. |\n| "critical" | Communicate problems that have to be resolved immediately for customers to complete a task. For example, using a different payment method if card details couldn’t be processed. Seeing these banners can be stressful for customers so be cautious about using them. |', + }, + ], + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Banner/examples/basic-banner.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Banner/examples/basic-banner.example.ts new file mode 100644 index 0000000000..830efd0b29 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Banner/examples/basic-banner.example.ts @@ -0,0 +1,11 @@ +import {extension, Banner} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const banner = root.createComponent(Banner, { + status: 'critical', + title: + 'Your payment details couldn’t be verified. Check your card details and try again.', + }); + + root.appendChild(banner); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/BlockLayout/BlockLayout.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/BlockLayout/BlockLayout.doc.ts new file mode 100644 index 0000000000..b97797defe --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/BlockLayout/BlockLayout.doc.ts @@ -0,0 +1,57 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'BlockLayout', + description: + 'BlockLayout is used to lay out content over multiple rows.\n\nBy default, all rows fill the available block space, sharing it equally.', + thumbnail: 'blocklayout-thumbnail.png', + isVisualComponent: true, + requires: '', + type: '', + definitions: [ + { + title: 'BlockLayoutProps', + description: '', + type: 'BlockLayoutProps', + }, + ], + category: 'UI components', + subCategory: 'Layout and structure', + defaultExample: { + image: 'blocklayout-default.png', + codeblock: { + title: 'Basic BlockLayout', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/BlockLayout/examples/basic-blocklayout.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-blocklayout.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'accessibility-roles', + title: 'Accessibility roles', + sectionContent: + '| Value | Description |\n| --- | --- |\n| "main" | Used to indicate the primary content. |\n| "header" | Used to indicate the component is a header. |\n| "footer" | Used to display information such as copyright information, navigation links, and privacy statements. |\n| "section" | Used to indicate a generic section. |\n| "complementary" | Used to designate a supporting section that relates to the main content. |\n| "navigation" | Used to identify major groups of links used for navigating. |\n| "orderedList" | Used to identify a list of ordered items. |\n| "listItem" | Used to identify an item inside a list of items. |\n| "unorderedList" | Used to identify a list of unordered items. |\n| "separator" | Used to indicates the component acts as a divider that separates and distinguishes sections of content. |\n| "status" | Used to define a live region containing advisory information for the user that is not important enough to be an alert. |\n| "alert" | Used for important, and usually time-sensitive, information. |', + }, + ], + related: [ + { + subtitle: 'Utility', + name: 'StyleHelper', + url: '/docs/api/checkout-ui-extensions/unstable/components/utilities/stylehelper', + type: 'utility', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/BlockLayout/examples/basic-blocklayout.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/BlockLayout/examples/basic-blocklayout.example.ts new file mode 100644 index 0000000000..995d94f748 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/BlockLayout/examples/basic-blocklayout.example.ts @@ -0,0 +1,16 @@ +import {extension, BlockLayout, View} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const blockLayout = root.createComponent( + BlockLayout, + { + rows: [60, 'fill'], + }, + [ + root.createComponent(View, {border: 'base', padding: 'base'}, '60'), + root.createComponent(View, {border: 'base', padding: 'base'}, 'fill'), + ], + ); + + root.appendChild(blockLayout); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/BlockSpacer/BlockSpacer.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/BlockSpacer/BlockSpacer.doc.ts new file mode 100644 index 0000000000..b44eb23e03 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/BlockSpacer/BlockSpacer.doc.ts @@ -0,0 +1,47 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'BlockSpacer', + description: + 'BlockSpacer is used to create empty block space, typically when variable spacing is needed between multiple elements.\n\nNote that you should favor BlockStack when spacing between all elements is the same.', + isVisualComponent: true, + thumbnail: 'blockspacer-thumbnail.png', + requires: '', + type: '', + definitions: [ + { + title: 'BlockSpacerProps', + description: '', + type: 'BlockSpacerProps', + }, + { + title: 'StyleHelper', + description: + 'Style is a helper for authoring conditional values for prop styles.\n\nWrite complex conditional styles based on one or more conditions, such as viewport sizes and interactive states, in a concise and expressive way.', + type: 'DocsStyle', + }, + ], + category: 'UI components', + subCategory: 'Layout and structure', + defaultExample: { + image: 'blockspacer-default.png', + codeblock: { + title: 'Basic BlockSpacer', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/BlockSpacer/examples/basic-blockspacer.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-blockspacer.example.ts', + language: 'js', + }, + ], + }, + }, + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/BlockSpacer/examples/basic-blockspacer.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/BlockSpacer/examples/basic-blockspacer.example.ts new file mode 100644 index 0000000000..c938c45eab --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/BlockSpacer/examples/basic-blockspacer.example.ts @@ -0,0 +1,11 @@ +import {extension, BlockSpacer, View} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const blockSpacer = root.createComponent(View, undefined, [ + root.createComponent(View, {border: 'base', padding: 'base'}, 'View'), + root.createComponent(BlockSpacer, {spacing: 'loose'}), + root.createComponent(View, {border: 'base', padding: 'base'}, 'View'), + ]); + + root.appendChild(blockSpacer); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/BlockStack/BlockStack.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/BlockStack/BlockStack.doc.ts new file mode 100644 index 0000000000..cc2998a9ab --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/BlockStack/BlockStack.doc.ts @@ -0,0 +1,67 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +import {getExample} from '../../helper.docs'; + +const disclosureAndAlignment = getExample( + 'ui-components/disclosure-and-alignment', + ['jsx', 'js'], +); + +const data: ReferenceEntityTemplateSchema = { + name: 'BlockStack', + description: 'BlockStack is used to vertically stack elements.', + thumbnail: 'blockstack-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'BlockStackProps', + description: '', + type: 'BlockStackProps', + }, + ], + category: 'UI components', + subCategory: 'Layout and structure', + defaultExample: { + image: 'blockstack-default.png', + codeblock: { + title: 'Basic BlockStack', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/BlockStack/examples/basic-blockstack.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-blockstack.example.ts', + language: 'js', + }, + ], + }, + }, + examples: { + description: '', + examples: [disclosureAndAlignment], + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'accessibility-roles', + title: 'Accessibility roles', + sectionContent: + '| Value | Description |\n| --- | --- |\n| "main" | Used to indicate the primary content. |\n| "header" | Used to indicate the component is a header. |\n| "footer" | Used to display information such as copyright information, navigation links, and privacy statements. |\n| "section" | Used to indicate a generic section. |\n| "complementary" | Used to designate a supporting section that relates to the main content. |\n| "navigation" | Used to identify major groups of links used for navigating. |\n| "orderedList" | Used to identify a list of ordered items. |\n| "listItem" | Used to identify an item inside a list of items. |\n| "unorderedList" | Used to identify a list of unordered items. |\n| "separator" | Used to indicates the component acts as a divider that separates and distinguishes sections of content. |\n| "status" | Used to define a live region containing advisory information for the user that is not important enough to be an alert. |\n| "alert" | Used for important, and usually time-sensitive, information. |', + }, + ], + related: [ + { + subtitle: 'Utility', + name: 'StyleHelper', + url: '/docs/api/checkout-ui-extensions/unstable/components/utilities/stylehelper', + type: 'utility', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/BlockStack/examples/basic-blockstack.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/BlockStack/examples/basic-blockstack.example.ts new file mode 100644 index 0000000000..7e48a27fe1 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/BlockStack/examples/basic-blockstack.example.ts @@ -0,0 +1,11 @@ +import {extension, BlockStack, View} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const blockStack = root.createComponent(BlockStack, undefined, [ + root.createComponent(View, {border: 'base', padding: 'base'}, 'View'), + root.createComponent(View, {border: 'base', padding: 'base'}, 'View'), + root.createComponent(View, {border: 'base', padding: 'base'}, 'View'), + ]); + + root.appendChild(blockStack); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Button/Button.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Button/Button.doc.ts new file mode 100644 index 0000000000..2d6f871288 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Button/Button.doc.ts @@ -0,0 +1,64 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Button', + description: + 'Buttons are used for actions, such as “Add”, “Continue”, “Pay now”, or “Save”.', + thumbnail: 'button-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'ButtonProps', + description: '', + type: 'ButtonProps', + }, + ], + category: 'UI components', + subCategory: 'Actions', + defaultExample: { + image: 'button-default.png', + codeblock: { + title: 'Basic Button', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Button/examples/basic-button.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-button.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'appearance', + title: 'Appearance', + sectionContent: + '| Value | Description |\n| --- | --- |\n| "critical" | Conveys a problem has arisen. |\n| "monochrome" | Takes the color of its parent.|', + }, + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + '**Content Best Practices**\n\n- Clearly label each button to accurately represent the action associated with it.\n\n- Use strong actionable verbs at the beginning of button text to make it clear to the user what action will occur when the button is clicked.\n\n**Hierarchy Best Practices**\n\n- Establish a visual hierarchy between buttons to minimize confusion and help users understand which actions are most important.\n\n- Use only one high-emphasis button (primary button) per context to make it clear that other buttons have less importance.\n\n- Use lower-emphasis buttons for secondary calls to action.\n\n- Use primary buttons for actions that progress users through checkout such as “Pay now” or for concluding an action in a modal such as “Apply”.\n\n- Use secondary buttons to provide alternative actions to the primary button, without disrupting the primary flow such as “Track your order”.\n\n- Use plain buttons for least prominent, non-critical actions such as “Login to your account”.\n\n**When to Use Buttons**\n\n- Use buttons to communicate actions the user can take.\n\n- Use buttons to allow users to interact with the page.\n\n**When Not to Use Buttons**\n\n- Don’t use buttons as navigational elements. Use links instead when the desired action is to take the user to a new page.', + }, + ], + related: [ + { + name: 'Link', + subtitle: 'Component', + url: 'link', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Button/examples/basic-button.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Button/examples/basic-button.example.ts new file mode 100644 index 0000000000..4d8191fb7d --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Button/examples/basic-button.example.ts @@ -0,0 +1,11 @@ +import {extension, Button} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const button = root.createComponent( + Button, + {onPress: () => console.log('onPress event')}, + 'Pay now', + ); + + root.appendChild(button); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Card/Card.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Card/Card.doc.ts index 05349256ec..b8dcb5688c 100644 --- a/packages/ui-extensions/src/surfaces/customer-account/components/Card/Card.doc.ts +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Card/Card.doc.ts @@ -15,7 +15,8 @@ const data: ReferenceEntityTemplateSchema = { type: 'CardProps', }, ], - category: 'components', + category: 'UI components', + subCategory: 'Layout and structure', defaultExample: { image: 'card-preview.png', altText: diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Chat/Chat.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/Chat.doc.ts new file mode 100644 index 0000000000..4fbe6edd61 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/Chat.doc.ts @@ -0,0 +1,208 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Chat', + description: ` +Use the Chat component to create real-time chat applications. +`, + thumbnail: 'chat-thumbnail.png', + requires: + 'access to the **Chat in checkout extensions** scope. Request access in the Partner Dashboard.', + isVisualComponent: true, + type: 'component', + definitions: [ + { + title: 'ChatProps', + description: '', + type: 'ChatProps', + }, + { + title: 'App Bridge for checkout', + description: ` +The App Bridge script for checkout provides APIs that enables a secure communication channel between the Shopify checkout and the embedded application within the Chat iframe. It also offers convenient methods to perform common actions like resizing the iframe from within the application. + +After App Bridge is [set up](#about-app-bridge) in your app, you have access to the \`shopify\` global variable. This variable exposes the following App Bridge functionalities and configuration information: + `, + type: 'AppBridge', + }, + ], + category: 'UI components', + subCategory: 'Overlays', + defaultExample: { + image: 'chat-component-minimized.png', + codeblock: { + title: 'Basic Chat', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Chat/examples/basic-chat.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-chat.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'src-and-query-parameters', + title: 'Chat source and query parameters', + sectionContent: ` +The \`src\` of the iframe rendered by Chat is provided by the \`preloads\` \`chat\` key in the extension configuration file. Shopify automatically appends query parameters to the URL which allows developers to verify the authenticity of the request and the identity of the merchant. We guarantee these tokens are valid and signed by Shopify. + +#### id_token +The ID token providing a set of claims as a signed [JSON Web Token (JWT)](https://openid.net/specs/openid-connect-core-1_0.html#IDToken%5C) with a TTL of 5 minutes. It can be used to retrieve merchants information on the backend as well as ensure that requests came from a Shopify authenticated source. See the [ID Token documentation](https://shopify.dev/docs/apps/build/authentication-authorization/session-tokens) for more information. + +#### locale +The locale of the shop that's embedding the app, i.e. \`en-CA\`. This information is also available in the \`shopify\` global variable under \`config\`. + +#### handle +The unique handle name of the UI extension as defined by the developer. This information is also available in the \`shopify\` global variable under \`extension\`. +`, + codeblock: { + title: 'Chat source', + tabs: [ + { + title: 'shopify.extension.toml', + code: './examples/shopify-extension-toml.example.toml', + language: 'toml', + }, + ], + }, + }, + { + type: 'Generic', + anchorLink: 'chat-dimensions', + title: 'Chat dimensions', + image: 'chat-component-iframe-clipping.png', + sectionContent: ` +To provide developers with the most flexibility when it comes to responsive changes, the iframe rendered in the page by \`Chat\` takes the full width and height of the browser window. Only a specific part of the iframe is visible, the rest is clipped. + +The \`inlineSize\` and \`blockSize\` values set on the Chat component or changed through the App Bridge \`resizeTo()\` method dictates the bounding box of the visible part. That box is fixed and positioned in the bottom right corner of the iframe. + +Your application can rely on the window's dimension to change styles or apply specific behaviors to different window sizes. This allow developers to style their app as if as if the application would be outside an iframe. For example, CSS media queries can now work within the iframe. +`, + }, + { + type: 'Generic', + anchorLink: 'about-app-bridge', + title: 'Getting started with App Bridge for checkout', + sectionContent: ` +You must add App Bridge to your hosted chat application by including the script tag pointing to the \`app-bridge-checkout.js\` as the first script in the \`\` section as seen in the example. The script loads directly from Shopify and keeps itself up-to-date. + `, + codeblock: { + title: 'Hosted chat application', + tabs: [ + { + code: './examples/include-app-bridge.example.html', + language: 'html', + }, + ], + }, + }, + { + type: 'Generic', + anchorLink: 'global-variable', + title: 'App Bridge\u2019s global variable', + sectionContent: ` +After App Bridge is set up in your app, you have access to the \`shopify\` global variable. This variable exposes various App Bridge functionalities, such as resizing the iframe or retrieving details of the shop. + +The [reference](#app%20bridge%20for%20checkout) above list all the available methods and properties. + +Alternatively, to explore all the functionality available on the \`shopify\` global variable: + +* Open the Chrome developer console while in checkout. +* Switch the frame context to your app's iframe. +* Enter \`shopify\` in the console. +`, + codeblock: { + title: 'shopify', + tabs: [ + { + title: 'config', + code: './examples/app-bridge-shopify-config.example.js', + language: 'js', + }, + ], + }, + }, + { + type: 'Generic', + anchorLink: 'app-bridge-css-api', + title: 'App Bridge\u2019s CSS API', + sectionContent: ` +Since the Chat iframe is clipped and fills the whole window as seen in the previous section, using percentage based sizes for its UI elements will most likely resolves in clipped content. As mentioned in the UX guidelines, Chat is constraint to specific [maximum sizes on large and small screens](/docs/apps/build/checkout/chat/ux-for-chat#build-within-the-chat-component-dimensions) set by Shopify. Setting a 100% width on an element will not be constraint to the visible size of the iframe, but to the whole window. + +To remediate this issue, through App Bridge we offer a set of [CSS custom properties](https://developer.mozilla.org/en-US/docs/Web/CSS/--*) with all the maximum dimensions defined in our UX guidelines. You can use these custom properties whether in Javascript or in the CSS of you application to set protections against overflowing content while using percentage based sizes. Using these properties will also reduce regressions if Shopify ever changes the maximum dimensions. +`, + codeblock: { + title: 'App Bridge CSS API', + tabs: [ + { + title: 'Custom properties', + code: './examples/app-bridge-css.example.css', + language: 'css', + }, + { + title: 'style.css', + code: './examples/chat-custom-properties-css.example.css', + language: 'css', + }, + ], + }, + }, + ], + examples: { + description: '', + examples: [ + { + description: + 'A very common action in your application will be to request a resize of the iframe. This is done through the App Bridge `resizeTo()` method. The following example resizes the iframe following the click of an activator button to show a dialog window.', + codeblock: { + title: 'Resize the Chat iframe from the hosted application', + tabs: [ + { + title: 'UI Extension', + code: './examples/app-bridge-resize.example.tsx', + language: 'tsx', + }, + { + title: 'Hosted chat application', + code: './examples/app-bridge-resize.example.html', + language: 'html', + }, + ], + }, + }, + { + description: ` +Information can be passed between the hosted application and the UI extension through App Bridge. [Extensions API](/docs/api/customer-account-ui-extensions/unstable#extension-apis) are available in the UI extension and can be shared through that method. + `, + codeblock: { + title: + 'Communicate information between the hosted application and the UI extension', + tabs: [ + { + title: 'UI Extension', + code: './examples/app-bridge-communication.example.tsx', + language: 'tsx', + }, + { + title: 'Hosted chat application', + code: './examples/app-bridge-communication.example.js', + language: 'js', + }, + ], + }, + }, + ], + }, + + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/app-bridge-communication.example.js b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/app-bridge-communication.example.js new file mode 100644 index 0000000000..8451403216 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/app-bridge-communication.example.js @@ -0,0 +1,18 @@ +// Create a variable to store the buyer's first name. +// We'll be able to use this to personalize the chat experience. +let buyerFirstName; + +// In the hosted application Javascript, listen for messages from the UI extension. +shopify.extension.port.onmessage = async (event) => { + // if the message's data has a ping action, respond with a pong + if (event.data.action === 'ping') { + buyerFirstName = event.data.buyer.firstName; + + await shopify.extension.port.postMessage({ + action: 'pong', + }); + } +}; + +// Ensure the messagePort is ready to start sending and receiving messages. +shopify.extension.port.start(); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/app-bridge-communication.example.tsx b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/app-bridge-communication.example.tsx new file mode 100644 index 0000000000..ac1b154899 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/app-bridge-communication.example.tsx @@ -0,0 +1,34 @@ +import { + reactExtension, + Chat, +} from '@shopify/ui-extensions-react/customer-account'; +import {retain} from '@remote-ui/rpc'; +import type {ReadyEvent} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension('customer-account.page.render', () => ( + +)); + +function Extension() { + let postMessage; + + return ( + { + postMessage = postMessageParam; + retain(postMessage); + + postMessage({ + action: 'ping', + }); + }} + onMessage={(event: Event) => { + if (event.data.action === 'pong') { + console.log('Messaging channel successful'); + } + }} + /> + ); +} diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/app-bridge-css.example.css b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/app-bridge-css.example.css new file mode 100644 index 0000000000..3a1d2c3064 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/app-bridge-css.example.css @@ -0,0 +1,16 @@ +:root { + --shopify-checkout-chat-minimized-max-inline-size: 224px; + --shopify-checkout-chat-minimized-max-block-size: 72px; + --shopify-checkout-chat-maximized-max-inline-size: 415px; + --shopify-checkout-chat-maximized-max-block-size: 700px; + + @media screen and (max-width: 569px) { + --shopify-checkout-chat-maximized-max-inline-size: 100dvi; + --shopify-checkout-chat-maximized-max-block-size: 93dvb; + + @supports not (inline-size: 100dvi) { + --shopify-checkout-chat-maximized-max-inline-size: 100vi; + --shopify-checkout-chat-maximized-max-block-size: 93vb; + } + } +} diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/app-bridge-resize.example.html b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/app-bridge-resize.example.html new file mode 100644 index 0000000000..34b5cfe1de --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/app-bridge-resize.example.html @@ -0,0 +1,48 @@ + + + + + + + + + How can we help you today? + + + + + + + diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/app-bridge-resize.example.tsx b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/app-bridge-resize.example.tsx new file mode 100644 index 0000000000..4c64b9c6a3 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/app-bridge-resize.example.tsx @@ -0,0 +1,9 @@ +import {reactExtension, Chat} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension('customer-account.page.render', () => ( + +)); + +function Extension() { + return ; +} diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/app-bridge-shopify-config.example.js b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/app-bridge-shopify-config.example.js new file mode 100644 index 0000000000..54096e0254 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/app-bridge-shopify-config.example.js @@ -0,0 +1,2 @@ +shopify.config.locale; +// => 'en-CA' diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/basic-chat.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/basic-chat.example.ts new file mode 100644 index 0000000000..6fea923fa9 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/basic-chat.example.ts @@ -0,0 +1,13 @@ +import {extension, Chat} from '@shopify/ui-extensions/customer-account'; + +// This component requires the configuration of the `extensions.targeting.preloads.chat` in the extensions configuration file. +// Its value will be used as the `src` attribute of the Chat component. + +export default extension('customer-account.page.render', (root) => { + const chat = root.createComponent(Chat, { + inlineSize: 100, + blockSize: 50, + }); + + root.appendChild(chat); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/chat-custom-properties-css.example.css b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/chat-custom-properties-css.example.css new file mode 100644 index 0000000000..20e873a61c --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/chat-custom-properties-css.example.css @@ -0,0 +1,9 @@ +.activator { + inline-size: 100%; + max-inline-size: var(--shopify-checkout-chat-minimized-max-inline-size); +} + +.dialog { + inline-size: 100%; + max-inline-size: var(--shopify-checkout-chat-maximized-max-inline-size); +} diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/include-app-bridge.example.html b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/include-app-bridge.example.html new file mode 100644 index 0000000000..ca6f00eb3b --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/include-app-bridge.example.html @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/shopify-extension-toml.example.toml b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/shopify-extension-toml.example.toml new file mode 100644 index 0000000000..7760a9a1c6 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Chat/examples/shopify-extension-toml.example.toml @@ -0,0 +1,5 @@ +[[extensions.targeting]] +target = "purchase.checkout.chat.render" + + [extensions.targeting.preloads] + chat = "https://my-chat-application.com" diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Checkbox/Checkbox.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Checkbox/Checkbox.doc.ts new file mode 100644 index 0000000000..1b2ab25b8b --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Checkbox/Checkbox.doc.ts @@ -0,0 +1,49 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +import {getExample} from '../../helper.docs'; + +const checkboxLinks = getExample('ui-components/checkbox-links', ['jsx', 'js']); + +const data: ReferenceEntityTemplateSchema = { + name: 'Checkbox', + description: + 'Use checkboxes to give customers a single binary option, such as signing up for marketing, or agreeing to terms and conditions.', + thumbnail: 'checkbox-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'CheckboxProps', + description: '', + type: 'CheckboxProps', + }, + ], + category: 'UI components', + subCategory: 'Forms', + defaultExample: { + image: 'checkbox-default.png', + codeblock: { + title: 'Basic Checkbox', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Checkbox/examples/basic-checkbox.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-checkbox.example.ts', + language: 'js', + }, + ], + }, + }, + examples: { + description: '', + examples: [checkboxLinks], + }, + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Checkbox/examples/basic-checkbox.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Checkbox/examples/basic-checkbox.example.ts new file mode 100644 index 0000000000..748708c209 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Checkbox/examples/basic-checkbox.example.ts @@ -0,0 +1,11 @@ +import {extension, Checkbox} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const checkbox = root.createComponent( + Checkbox, + {id: 'checkbox', name: 'checkbox'}, + 'Save this information for next time', + ); + + root.appendChild(checkbox); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Choice/Choice.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Choice/Choice.doc.ts new file mode 100644 index 0000000000..d72e6e27d2 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Choice/Choice.doc.ts @@ -0,0 +1,57 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Choice', + description: + 'Options inside a `ChoiceList`.\n\nThe wrapping `ChoiceList` component will dictate if the choice renders as radio buttons or checkboxes.', + thumbnail: 'choice-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'ChoiceProps', + description: '', + type: 'ChoiceProps', + }, + ], + category: 'UI components', + subCategory: 'Forms', + defaultExample: { + image: 'choice-default.png', + codeblock: { + title: 'Basic Choice', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Choice/examples/basic-choice.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-choice.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + '- Include a title that either tells customers what to do or explains their available options.\n\n- Label options clearly based on what the option will do.\n\n- Avoid options that contradict each other when you’re allowing for multiple selections.', + }, + ], + related: [ + { + name: 'ChoiceList', + subtitle: 'Component', + url: 'choicelist', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Choice/examples/basic-choice.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Choice/examples/basic-choice.example.ts new file mode 100644 index 0000000000..0b0fe19fb6 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Choice/examples/basic-choice.example.ts @@ -0,0 +1,39 @@ +import { + extension, + ChoiceList, + Choice, + InlineStack, +} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const choiceList = root.createComponent( + ChoiceList, + { + name: 'ship', + value: 'ship-1', + onChange: (value) => { + console.log(`onChange event with value: ${value}`); + }, + }, + [root.createComponent(Choice, {id: 'ship-1'}, 'Ship')], + ); + + const multipleChoiceList = root.createComponent( + ChoiceList, + { + name: 'gift', + value: ['gift-1'], + onChange: (value) => { + console.log(`onChange event with value: ${value}`); + }, + }, + [root.createComponent(Choice, {id: 'gift-1'}, 'Gift message')], + ); + + const layout = root.createComponent(InlineStack, undefined, [ + choiceList, + multipleChoiceList, + ]); + + root.appendChild(layout); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ChoiceList/ChoiceList.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ChoiceList/ChoiceList.doc.ts new file mode 100644 index 0000000000..bbb6e3324f --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ChoiceList/ChoiceList.doc.ts @@ -0,0 +1,103 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +import {getExample} from '../../helper.docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'ChoiceList', + description: + 'Use choice lists to present a list of choices where buyers can make a single selection or multiple selections.', + thumbnail: 'choicelist-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'ChoiceListProps', + description: '', + type: 'ChoiceListProps', + }, + ], + category: 'UI components', + subCategory: 'Forms', + defaultExample: { + image: 'choicelist-default.png', + codeblock: { + title: 'Basic ChoiceList', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/ChoiceList/examples/basic-choicelist.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-choicelist.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: '', + sectionSubContent: [ + { + title: 'Content', + sectionContent: `- Include a title that either tells customers what to do or explains their available options.\n\n- Label options clearly based on what the option will do.\n\n- Avoid options that contradict each other when you’re allowing for multiple selections.`, + }, + { + title: 'Types of choices', + sectionContent: + "![A screenshot of two examples for base and group variants](/assets/landing-pages/templated-apis/checkout-ui-extensions/ui-components/choicelist-bp-choicetypes.png)\n- The `base` variant is suitable for straightforward options, such as binary choices like 'yes' or 'no' answers. For simple options that require minimal visual emphasis, the base variant is the recommended choice.\n- The `group` variant provides increased emphasis on choices through the use of colors, borders, and dividers. If the goal is to draw attention to the available options, the group variant is the ideal choice.", + }, + { + title: 'Flexibility vs. cohesive experience', + sectionContent: + '- The `base` variant offers the flexibility to compose ChoiceLists with custom layouts, allowing for greater design composition possibilities.\n- Given the high level of flexibility offered by the `base` variant, prioritizing accessibility in the implementation is crucial. It is recommended to avoid using buttons or pressable components within the choices.\n- The `group` variant adheres to a defined structure and provides excellent adaptability to align with the merchant’s brand. It is the ideal choice when strong cohesion with the merchant’s branding is required. Additionally, depending on the placement of the `ChoiceList`, Checkout will automatically update its appearance to seamlessly adapt to the surface it is on.', + }, + { + title: 'Using details', + sectionContent: + '![A screenshot of an example of using details](/assets/landing-pages/templated-apis/checkout-ui-extensions/ui-components/choicelist-bp-details.png)\n- The `details` area should be used only when additional input is required from the customer, specifically related to the choice they have made.\n- If the `details` area contains lengthy content, consider placing it outside of the `ChoiceList` to ensure that customers can easily digest the information.', + }, + { + title: 'Long lists of options', + sectionContent: + '- When faced with a considerable number of options, customers may feel overwhelmed, and it can consume valuable interface space. To address this, consider utilizing components like the [Select](/docs/api/checkout-ui-extensions/components/forms/select) component to condense options, or employ the [Disclosure](/docs/api/checkout-ui-extensions/components/interactive/disclosure) component to progressively reveal more choices upon customer request. Strategies such as paging, filtering, and searching can be employed to enhance usability.', + }, + { + title: 'Clickable rows', + sectionContent: + '- If an entire row needs to be clickable, utilize the `group` variant, as it is specifically designed to enable clickable rows. In this scenario, the base variant may not provide the desired functionality, as only its content elements can be clicked, not the entire row. Attempting to use buttons or pressables to make an entire row clickable could lead to accessibility issues.', + }, + ], + }, + ], + examples: { + description: '', + examples: [ + getExample('ui-components/choicelist-survey', ['jsx', 'js']), + getExample('ui-components/choicelist-details', ['jsx', 'js']), + getExample('ui-components/choicelist-time-picking', ['jsx', 'js']), + ], + }, + related: [ + { + name: 'Choice', + subtitle: 'Component', + url: 'choice', + type: 'Component', + }, + { + name: 'Checkbox', + subtitle: 'Component', + url: 'checkbox', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ChoiceList/examples/basic-choicelist.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ChoiceList/examples/basic-choicelist.example.ts new file mode 100644 index 0000000000..995b1f0957 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ChoiceList/examples/basic-choicelist.example.ts @@ -0,0 +1,81 @@ +import { + extension, + InlineStack, + ChoiceList, + Choice, + BlockStack, + Icon, +} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const inlineStack = root.createComponent(InlineStack, undefined, [ + root.createComponent( + ChoiceList, + { + name: 'group-single', + variant: 'group', + value: 'ship', + onChange: (value) => { + console.log(`onChange event with value: ${value}`); + }, + }, + [ + root.createComponent( + Choice, + { + secondaryContent: root.createComponent(Icon, {source: 'truck'}), + id: 'ship', + }, + 'Ship', + ), + root.createComponent( + Choice, + { + secondaryContent: root.createComponent(Icon, {source: 'marker'}), + id: 'ship-to-pickup-point', + }, + 'Ship to pickup point', + ), + root.createComponent( + Choice, + { + secondaryContent: root.createComponent(Icon, {source: 'store'}), + id: 'pick-up', + }, + 'Pick up in store', + ), + ], + ), + root.createComponent( + ChoiceList, + { + name: 'base-multiple', + value: ['remember-me'], + onChange: (value) => { + console.log(`onChange event with value: ${value}`); + }, + }, + [ + root.createComponent(BlockStack, undefined, [ + root.createComponent( + Choice, + {id: 'remember-me'}, + 'Save this information for next time', + ), + root.createComponent( + Choice, + {id: 'email-me'}, + 'Email me with news and offers', + ), + root.createComponent( + Choice, + {id: 'text-me'}, + 'Text me with news and offers', + ), + ]), + ], + ), + ]); + + root.appendChild(inlineStack); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ClipboardItem/ClipboardItem.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ClipboardItem/ClipboardItem.doc.ts new file mode 100644 index 0000000000..41d5be8d68 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ClipboardItem/ClipboardItem.doc.ts @@ -0,0 +1,75 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +import {getExample} from '../../helper.docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'ClipboardItem', + description: + 'Enables clipboard functionality when its `id` is referenced by the `activateTarget` property of a `Button`, `Pressable`, or `Link` component. When activated, it copies the text to the clipboard and displays a `Tooltip` confirmation. \n\n `ClipboardItem` is a non-rendering component.', + requires: '', + isVisualComponent: true, + thumbnail: 'clipboard-basic.png', + type: '', + definitions: [ + { + title: 'ClipboardItemProps', + description: '', + type: 'ClipboardItemProps', + }, + ], + category: 'UI components', + subCategory: 'Actions', + defaultExample: { + image: 'clipboard-basic.png', + codeblock: { + title: 'Basic Copy to Clipboard', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/ClipboardItem/examples/basic-clipboarditem.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-clipboarditem.example.ts', + language: 'js', + }, + ], + }, + }, + examples: { + description: '', + examples: [ + getExample('ui-components/clipboarditem-qrcode', ['jsx', 'js']), + getExample('ui-components/clipboarditem-oncopy', ['jsx', 'js']), + ], + }, + related: [ + { + name: 'Button', + subtitle: 'Component', + url: 'button', + type: 'Component', + }, + { + name: 'Pressable', + subtitle: 'Component', + url: 'pressable', + type: 'Component', + }, + { + name: 'Link', + subtitle: 'Component', + url: 'link', + type: 'Component', + }, + { + name: 'QR Code', + subtitle: 'Component', + url: 'qrcode', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ClipboardItem/examples/basic-clipboarditem.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ClipboardItem/examples/basic-clipboarditem.example.ts new file mode 100644 index 0000000000..3c19ca0ea6 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ClipboardItem/examples/basic-clipboarditem.example.ts @@ -0,0 +1,21 @@ +import { + extension, + ClipboardItem, + Button, +} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const button = root.createComponent( + Button, + { + activateTarget: 'discount-code', + }, + 'Copy discount code', + ); + const clipboardItem = root.createComponent(ClipboardItem, { + text: 'SAVE 25', + id: 'discount-code', + }); + root.appendChild(button); + root.appendChild(clipboardItem); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ConsentCheckbox/ConsentCheckbox.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ConsentCheckbox/ConsentCheckbox.doc.ts new file mode 100644 index 0000000000..206ca7e8fb --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ConsentCheckbox/ConsentCheckbox.doc.ts @@ -0,0 +1,72 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'ConsentCheckbox', + description: + "Use buyer consent checkboxes for collecting the buyer's approval for a given policy.", + thumbnail: 'consent-checkbox-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'ConsentCheckboxProps', + description: '', + type: 'ConsentCheckboxProps', + }, + ], + category: 'UI components', + subCategory: 'Forms', + defaultExample: { + image: 'consent-checkbox-default.png', + codeblock: { + title: 'Basic ConsentCheckbox', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/ConsentCheckbox/examples/basic-consent-checkbox.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-consent-checkbox.example.ts', + language: 'js', + }, + ], + }, + }, + examples: { + description: + "Use buyer consent checkboxes in conjunction with buyer consent phone fields for collecting the buyer's approval for a given policy.\n\nThe consent phone field is not required in order to use the consent checkbox component. This example demonstrates how they can be used together.", + examples: [ + { + image: 'consent-combo-example.png', + codeblock: { + title: 'ConsentCheckbox with ConsentPhoneField', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/ConsentCheckbox/examples/with-consent-phone-field-consent-checkbox.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/with-consent-phone-field-consent-checkbox.example.ts', + language: 'js', + }, + ], + }, + }, + ], + }, + related: [ + { + name: 'ConsentCheckbox', + subtitle: 'Component', + url: 'consent-checkbox', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ConsentCheckbox/examples/basic-consent-checkbox.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ConsentCheckbox/examples/basic-consent-checkbox.example.ts new file mode 100644 index 0000000000..64fc9b5cc7 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ConsentCheckbox/examples/basic-consent-checkbox.example.ts @@ -0,0 +1,11 @@ +import {extension, ConsentCheckbox} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const consentCheckbox = root.createComponent( + ConsentCheckbox, + {policy: 'sms-marketing'}, + 'Text me with news and promotions', + ); + + root.appendChild(consentCheckbox); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ConsentCheckbox/examples/with-consent-phone-field-consent-checkbox.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ConsentCheckbox/examples/with-consent-phone-field-consent-checkbox.example.ts new file mode 100644 index 0000000000..cbf8bedc4f --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ConsentCheckbox/examples/with-consent-phone-field-consent-checkbox.example.ts @@ -0,0 +1,43 @@ +import { + extension, + BlockStack, + ConsentCheckbox, + ConsentPhoneField, + InlineStack, + InlineSpacer, +} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const consentCheckbox = root.createComponent( + ConsentCheckbox, + { + policy: 'sms-marketing', + }, + 'Text me with news and offers', + ); + + const inlineSpacer = root.createComponent(InlineSpacer, { + spacing: 'extraTight', + }); + + const consentPhoneField = root.createComponent(ConsentPhoneField, { + label: 'Phone', + policy: 'sms-marketing', + }); + + const inlineStack = root.createComponent( + InlineStack, + { + inlineAlignment: 'start', + padding: ['none', 'none', 'none', 'tight'], + }, + [inlineSpacer, consentPhoneField], + ); + + const layout = root.createComponent(BlockStack, undefined, [ + consentCheckbox, + inlineStack, + ]); + + root.appendChild(layout); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ConsentPhoneField/ConsentPhoneField.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ConsentPhoneField/ConsentPhoneField.doc.ts new file mode 100644 index 0000000000..1ff432afcb --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ConsentPhoneField/ConsentPhoneField.doc.ts @@ -0,0 +1,72 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'ConsentPhoneField', + description: + 'Display a phone field for customers to sign up for text message marketing, noting that the phone field value will be automatically saved during checkout.', + thumbnail: 'consent-phonefield-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'ConsentPhoneFieldProps', + description: '', + type: 'ConsentPhoneFieldProps', + }, + ], + category: 'UI components', + subCategory: 'Forms', + defaultExample: { + image: 'consent-phonefield-default.png', + codeblock: { + title: 'Basic ConsentPhoneField', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/ConsentPhoneField/examples/basic-consent-phone-field.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-consent-phone-field.example.ts', + language: 'js', + }, + ], + }, + }, + examples: { + description: + "Use buyer consent phone fields in conjunction with buyer consent checkboxes for collecting the buyer's approval for a given policy.\n\nThe consent phone field component is not required in order to use the consent checkbox component. This example demonstrates how they can be used together.", + examples: [ + { + image: 'consent-combo-example.png', + codeblock: { + title: 'ConsentCheckbox with ConsentPhoneField', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/ConsentPhoneField/examples/with-consent-checkbox-consent-phone-field.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/with-consent-checkbox-consent-phone-field.example.ts', + language: 'js', + }, + ], + }, + }, + ], + }, + related: [ + { + name: 'ConsentCheckbox', + subtitle: 'Component', + url: 'consent-checkbox', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ConsentPhoneField/examples/basic-consent-phone-field.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ConsentPhoneField/examples/basic-consent-phone-field.example.ts new file mode 100644 index 0000000000..e4c048fd1e --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ConsentPhoneField/examples/basic-consent-phone-field.example.ts @@ -0,0 +1,10 @@ +import {extension, ConsentPhoneField} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const consentPhoneField = root.createComponent(ConsentPhoneField, { + label: 'Phone', + policy: 'sms-marketing', + }); + + root.appendChild(consentPhoneField); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ConsentPhoneField/examples/with-consent-checkbox-consent-phone-field.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ConsentPhoneField/examples/with-consent-checkbox-consent-phone-field.example.ts new file mode 100644 index 0000000000..cbf8bedc4f --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ConsentPhoneField/examples/with-consent-checkbox-consent-phone-field.example.ts @@ -0,0 +1,43 @@ +import { + extension, + BlockStack, + ConsentCheckbox, + ConsentPhoneField, + InlineStack, + InlineSpacer, +} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const consentCheckbox = root.createComponent( + ConsentCheckbox, + { + policy: 'sms-marketing', + }, + 'Text me with news and offers', + ); + + const inlineSpacer = root.createComponent(InlineSpacer, { + spacing: 'extraTight', + }); + + const consentPhoneField = root.createComponent(ConsentPhoneField, { + label: 'Phone', + policy: 'sms-marketing', + }); + + const inlineStack = root.createComponent( + InlineStack, + { + inlineAlignment: 'start', + padding: ['none', 'none', 'none', 'tight'], + }, + [inlineSpacer, consentPhoneField], + ); + + const layout = root.createComponent(BlockStack, undefined, [ + consentCheckbox, + inlineStack, + ]); + + root.appendChild(layout); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/CustomerAccountAction/CustomerAccountAction.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/CustomerAccountAction/CustomerAccountAction.doc.ts index 0737874823..3df8e78ccc 100644 --- a/packages/ui-extensions/src/surfaces/customer-account/components/CustomerAccountAction/CustomerAccountAction.doc.ts +++ b/packages/ui-extensions/src/surfaces/customer-account/components/CustomerAccountAction/CustomerAccountAction.doc.ts @@ -27,7 +27,8 @@ const data: ReferenceEntityTemplateSchema = { type: 'Docs_CustomerAccountAction_Button_SecondaryAction', }, ], - category: 'components', + category: 'UI components', + subCategory: 'Actions', defaultExample: { image: 'customeraccountaction-preview.png', altText: diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/DateField/DateField.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/DateField/DateField.doc.ts new file mode 100644 index 0000000000..8756e97043 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/DateField/DateField.doc.ts @@ -0,0 +1,49 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'DateField', + description: 'Use a date field to get a date input from a customer.', + thumbnail: 'datefield-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'DateFieldProps', + description: '', + type: 'DateFieldProps', + }, + ], + category: 'UI components', + subCategory: 'Forms', + defaultExample: { + image: 'datefield-default.png', + codeblock: { + title: 'Basic DateField', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/DateField/examples/basic-datefield.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-datefield.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + '- Use clear and concise labels for the DateField component to help customers understand what information is expected.\n\n**When to use a DateField**\n\n- Use when the dates are memorable to the customer.\n\n- Use when all dates are available to be chosen by the customer.\n\n**When not to use a DateField**\n\n- Don’t use when customers require a visual representation of the dates, rather than manual entry, consider using a DatePicker component instead.\n\n- Don’t use when date availability logic is in place. Customers may find it difficult to determine which dates are available if they’re typing. Use a DatePicker instead.', + }, + ], + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/DateField/examples/basic-datefield.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/DateField/examples/basic-datefield.example.ts new file mode 100644 index 0000000000..7a5fb92ac6 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/DateField/examples/basic-datefield.example.ts @@ -0,0 +1,9 @@ +import {extension, DateField} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const datefield = root.createComponent(DateField, { + label: 'Select a date', + }); + + root.appendChild(datefield); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/DatePicker/DatePicker.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/DatePicker/DatePicker.doc.ts new file mode 100644 index 0000000000..7918018814 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/DatePicker/DatePicker.doc.ts @@ -0,0 +1,50 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'DatePicker', + description: + 'The DatePicker component is a calendar picker UI that allows users to select a single date or a date range', + thumbnail: 'datepicker-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'DatePickerProps', + description: '', + type: 'DatePickerProps', + }, + ], + category: 'UI components', + subCategory: 'Forms', + defaultExample: { + image: 'datepicker-default.png', + codeblock: { + title: 'Basic DatePicker', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/DatePicker/examples/basic-datepicker.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-datepicker.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + 'By adhering to these design guidelines, the DatePicker component can offer a seamless and efficient method for users to select dates, thereby enhancing the overall user experience.\n\n- Default to showing today’s date if available.\n\n- Display the first available date when selecting future dates.\n\n- To minimize errors, the process of selecting a date range may require multiple steps. Therefore, providing a way for users to explicitly confirm their selection is recommended.\n\n**When to use a DatePicker**\n\nThe DatePicker component is well-suited for the following scenarios:\n\n- Specifying shipping or delivery dates\n\n- Scheduling pick-up dates\n\n- Booking dates for service providers\n\n- Selecting event dates for ticket offerings\n\n- Specifying rental dates to determine start and end dates for renting a product\n\n**When not to use a DatePicker component**\n\nA DatePicker component might not be the most appropriate choice in the following situations:\n\n- When the date to be entered is several years in the future or the past.\n\n- When the date is easily memorable and can be quickly typed using the keyboard e.g. Date of birth.', + }, + ], + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/DatePicker/examples/basic-datepicker.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/DatePicker/examples/basic-datepicker.example.ts new file mode 100644 index 0000000000..1be0201dcc --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/DatePicker/examples/basic-datepicker.example.ts @@ -0,0 +1,9 @@ +import {extension, DatePicker} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const datepicker = root.createComponent(DatePicker, { + selected: '2021-06-01', + }); + + root.appendChild(datepicker); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Disclosure/Disclosure.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Disclosure/Disclosure.doc.ts new file mode 100644 index 0000000000..e00d17bd50 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Disclosure/Disclosure.doc.ts @@ -0,0 +1,61 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +import {getExample} from '../../helper.docs'; + +const disclosureAndAlignment = getExample( + 'ui-components/disclosure-and-alignment', + ['jsx', 'js'], +); + +const data: ReferenceEntityTemplateSchema = { + name: 'Disclosure', + description: + 'Disclosure is an optionally controlled component used to put long sections of information under content blocks that users can expand or collapse by pressing an activator. The activator can be specified as children using an action component (`Button`, `Link` or `Pressable`) or a form control (`Checkbox` or `Switch`) component. The content blocks can be specified as children inside a structure component (`View`, `InlineLayout`, `BlockStack`, `Grid`, etc.).\n\nThe library automatically applies the [WAI-ARIA Accordion pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/) to both the activator and the toggled content.', + thumbnail: 'disclosure-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'DisclosureProps', + description: '', + type: 'DisclosureProps', + }, + ], + category: 'UI components', + subCategory: 'Typography and content', + defaultExample: { + image: 'disclosure-default.png', + codeblock: { + title: 'Basic Disclosure', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Disclosure/examples/basic-disclosure.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-disclosure.example.ts', + language: 'js', + }, + ], + }, + }, + examples: { + description: '', + examples: [disclosureAndAlignment], + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + '- Disclosures should be initiated by the buyer.\n\n- Use disclosures to hide content until they are relevant to the buyer.\n\n- Avoid hiding critical information that buyers need to complete their checkout.\n\n- Keep content inside disclosures concise.\n\n- Avoid nesting of disclosures.\n\n- Keep the activator and the content it toggles in close proximity to each other.', + }, + ], + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Disclosure/examples/basic-disclosure.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Disclosure/examples/basic-disclosure.example.ts new file mode 100644 index 0000000000..c43f82c704 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Disclosure/examples/basic-disclosure.example.ts @@ -0,0 +1,19 @@ +import { + extension, + Button, + View, + Disclosure, +} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const disclosure = root.createComponent(Disclosure, {}, [ + root.createComponent(Button, {toggles: 'one'}, 'Toggle'), + root.createComponent( + View, + {border: 'base', padding: 'base', id: 'one'}, + 'Content', + ), + ]); + + root.appendChild(disclosure); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Divider/Divider.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Divider/Divider.doc.ts new file mode 100644 index 0000000000..cd881f23f3 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Divider/Divider.doc.ts @@ -0,0 +1,41 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Divider', + description: + 'A divider separates content and represents a thematic break between elements.', + thumbnail: 'divider-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'DividerProps', + description: '', + type: 'DividerProps', + }, + ], + category: 'UI components', + subCategory: 'Layout and structure', + defaultExample: { + image: 'divider-default.png', + codeblock: { + title: 'Basic Divider', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Divider/examples/basic-divider.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-divider.example.ts', + language: 'js', + }, + ], + }, + }, + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Divider/examples/basic-divider.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Divider/examples/basic-divider.example.ts new file mode 100644 index 0000000000..3aa02a41a4 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Divider/examples/basic-divider.example.ts @@ -0,0 +1,7 @@ +import {extension, Divider} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const divider = root.createComponent(Divider); + + root.appendChild(divider); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/DropZone/DropZone.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/DropZone/DropZone.doc.ts index f4a3b3499f..00d96c557a 100644 --- a/packages/ui-extensions/src/surfaces/customer-account/components/DropZone/DropZone.doc.ts +++ b/packages/ui-extensions/src/surfaces/customer-account/components/DropZone/DropZone.doc.ts @@ -2,7 +2,7 @@ import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; const data: ReferenceEntityTemplateSchema = { name: 'DropZone', - description: `Dropzone allows file uploads through drag-and-drop functionality into a designated area on a page, or by activating a button. At present, Dropzone does not offer image upload preview capabilities. The use of object URLs directly in an image component is not possible due to the extension and host operating on separate domains. + description: `DropZone allows file uploads through drag-and-drop functionality into a designated area on a page, or by activating a button. At present, DropZone does not offer image upload preview capabilities. The use of object URLs directly in an image component is not possible due to the extension and host operating on separate domains. \n Any element focused within the Dropzone component, including child elements such as the 'Add file' button, will initiate the file selector when the Enter or Spacebar key is pressed. `, thumbnail: 'dropzone-thumbnail.png', @@ -16,7 +16,8 @@ const data: ReferenceEntityTemplateSchema = { type: 'DropZoneProps', }, ], - category: 'components', + category: 'UI components', + subCategory: 'Forms', defaultExample: { image: 'dropzone-preview.png', altText: @@ -45,19 +46,19 @@ const data: ReferenceEntityTemplateSchema = { sectionContent: ` ### File storage -File storage for uploads must be implemented separately. Metafields and [the Customer Account API](https://shopify.dev/docs/api/customer/latest/mutations/metafieldsSet) can be utilized to store references to files alongside the relevant objects. +File storage for uploads must be implemented separately. Metafields and the corresponding [Checkout API](https://shopify.dev/docs/api/checkout-ui-extensions/latest/apis/metafields) or [Customer Accounts API](https://shopify.dev/docs/api/customer/latest/mutations/metafieldsSet) can be utilized to store references to files alongside the relevant objects. ### Mobile Remember that the drag and drop feature won't be effective on mobile devices. Adding a button can offer additional context and guide users through the next steps. -An example showing DropZone with custom content optimized for mobile devices +An example showing DropZone with custom content optimized for mobile devices ### Minimum size -To prevent cut-off text and spacing issues, the minimum size of a Dropzone should be 100px x 100px. +To prevent cut-off text and spacing issues, the minimum size of a Dropzone should be 100px by 100px. -An example showing DropZone with correct minimum size +An example showing DropZone with correct minimum size `, }, ], diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/DropZone/examples/basic-DropZone-js.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/DropZone/examples/basic-DropZone-js.example.ts index b59c045669..9a0d33c63e 100644 --- a/packages/ui-extensions/src/surfaces/customer-account/components/DropZone/examples/basic-DropZone-js.example.ts +++ b/packages/ui-extensions/src/surfaces/customer-account/components/DropZone/examples/basic-DropZone-js.example.ts @@ -1,13 +1,9 @@ import {DropZone, extension} from '@shopify/ui-extensions/customer-account'; -export default extension('customer-account.page.render', (root, api) => { - renderApp(root, api); -}); - -async function renderApp(root, api) { +export default extension('customer-account.page.render', (root) => { const dropZone = root.createComponent(DropZone, { accept: 'image/*', }); - root.append(dropZone); -} + root.appendChild(dropZone); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Form/Form.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Form/Form.doc.ts new file mode 100644 index 0000000000..549dbe6c2b --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Form/Form.doc.ts @@ -0,0 +1,50 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Form', + description: + 'The form component should be used to wrap one or more form controls. This component provides an "implicit submit" behavior, where customers can submit the form from any input by pressing "Enter" on their keyboards. This behavior is widely expected, and should be respected as often as possible.\n\nUnlike an HTML `form` element, this component does not support configuring the descendant fields to be submitted via HTTP automatically. Instead, you must provide an `onSubmit` callback that will perform the necessary HTTP requests in JavaScript.', + thumbnail: 'form-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'FormProps', + description: '', + type: 'FormProps', + }, + ], + category: 'UI components', + subCategory: 'Forms', + defaultExample: { + image: 'form-default.png', + codeblock: { + title: 'Basic Form', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Form/examples/basic-form.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-form.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + '- Wrap around all form input elements.\n\n- Forms can have only one submit button and it must be at the end of the form.', + }, + ], + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Form/examples/basic-form.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Form/examples/basic-form.example.ts new file mode 100644 index 0000000000..15a53c7c99 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Form/examples/basic-form.example.ts @@ -0,0 +1,48 @@ +import { + extension, + BlockSpacer, + Button, + Form, + Grid, + GridItem, + TextField, + View, +} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const fields = root.createComponent( + Grid, + {columns: ['50%', '50%'], spacing: 'base'}, + [ + root.createComponent( + View, + undefined, + root.createComponent(TextField, {label: 'First name'}), + ), + root.createComponent( + View, + undefined, + root.createComponent(TextField, {label: 'Last name'}), + ), + root.createComponent( + GridItem, + {columnSpan: 2}, + root.createComponent(TextField, {label: 'Company'}), + ), + ], + ); + const spacer = root.createComponent(BlockSpacer, {spacing: 'base'}); + const button = root.createComponent( + Button, + {accessibilityRole: 'submit'}, + 'Submit', + ); + + const form = root.createComponent( + Form, + {onSubmit: () => console.log('onSubmit event')}, + [fields, spacer, button], + ); + + root.appendChild(form); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Grid/Grid.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Grid/Grid.doc.ts new file mode 100644 index 0000000000..0cb43e6b12 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Grid/Grid.doc.ts @@ -0,0 +1,63 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Grid', + description: + 'Grid is used to lay out content in a matrix of rows and columns.', + thumbnail: 'grid-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'GridProps', + description: '', + type: 'GridProps', + }, + ], + category: 'UI components', + subCategory: 'Layout and structure', + defaultExample: { + image: 'grid-default.png', + codeblock: { + title: 'Basic Grid', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Grid/examples/basic-grid.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-grid.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'accessibility-roles', + title: 'Accessibility roles', + sectionContent: + '| Value | Description |\n| --- | --- |\n| "main" | Used to indicate the primary content. |\n| "header" | Used to indicate the component is a header. |\n| "footer" | Used to display information such as copyright information, navigation links, and privacy statements. |\n| "section" | Used to indicate a generic section. |\n| "complementary" | Used to designate a supporting section that relates to the main content. |\n| "navigation" | Used to identify major groups of links used for navigating. |\n| "orderedList" | Used to identify a list of ordered items. |\n| "listItem" | Used to identify an item inside a list of items. |\n| "unorderedList" | Used to identify a list of unordered items. |\n| "separator" | Used to indicates the component acts as a divider that separates and distinguishes sections of content. |\n| "status" | Used to define a live region containing advisory information for the user that is not important enough to be an alert. |\n| "alert" | Used for important, and usually time-sensitive, information. |', + }, + ], + related: [ + { + name: 'GridItem', + subtitle: 'Component', + url: 'grid', + type: 'Component', + }, + { + subtitle: 'Utility', + name: 'StyleHelper', + url: '/docs/api/checkout-ui-extensions/unstable/components/utilities/stylehelper', + type: 'utility', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Grid/examples/basic-grid.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Grid/examples/basic-grid.example.ts new file mode 100644 index 0000000000..90425a8b0c --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Grid/examples/basic-grid.example.ts @@ -0,0 +1,45 @@ +import {extension, Grid, View} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const grid = root.createComponent( + Grid, + { + columns: ['20%', 'fill', 'auto'], + rows: [300, 'auto'], + }, + [ + root.createComponent( + View, + {border: 'base', padding: 'base'}, + '20% / 300', + ), + root.createComponent( + View, + {border: 'base', padding: 'base'}, + 'fill / 300', + ), + root.createComponent( + View, + {border: 'base', padding: 'base'}, + 'auto / 300', + ), + root.createComponent( + View, + {border: 'base', padding: 'base'}, + '20% / auto', + ), + root.createComponent( + View, + {border: 'base', padding: 'base'}, + 'fill / auto', + ), + root.createComponent( + View, + {border: 'base', padding: 'base'}, + 'auto / auto', + ), + ], + ); + + root.appendChild(grid); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/GridItem/GridItem.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/GridItem/GridItem.doc.ts new file mode 100644 index 0000000000..d72271df6e --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/GridItem/GridItem.doc.ts @@ -0,0 +1,63 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'GridItem', + description: + 'GridItem can be used as children of Grid.\n\nIt offers a way to span the element across a number of columns and rows.', + thumbnail: 'griditem-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'GridItemProps', + description: '', + type: 'GridItemProps', + }, + ], + category: 'UI components', + subCategory: 'Layout and structure', + defaultExample: { + image: 'griditem-default.png', + codeblock: { + title: 'Basic GridItem', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/GridItem/examples/basic-griditem.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-griditem.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'accessibility-roles', + title: 'Accessibility roles', + sectionContent: + '| Value | Description |\n| --- | --- |\n| "main" | Used to indicate the primary content. |\n| "header" | Used to indicate the component is a header. |\n| "footer" | Used to display information such as copyright information, navigation links, and privacy statements. |\n| "section" | Used to indicate a generic section. |\n| "complementary" | Used to designate a supporting section that relates to the main content. |\n| "navigation" | Used to identify major groups of links used for navigating. |\n| "orderedList" | Used to identify a list of ordered items. |\n| "listItem" | Used to identify an item inside a list of items. |\n| "unorderedList" | Used to identify a list of unordered items. |\n| "separator" | Used to indicates the component acts as a divider that separates and distinguishes sections of content. |\n| "status" | Used to define a live region containing advisory information for the user that is not important enough to be an alert. |\n| "alert" | Used for important, and usually time-sensitive, information. |', + }, + ], + related: [ + { + name: 'Grid', + subtitle: 'Component', + url: 'grid', + type: 'Component', + }, + { + subtitle: 'Utility', + name: 'StyleHelper', + url: '/docs/api/checkout-ui-extensions/unstable/components/utilities/stylehelper', + type: 'utility', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/GridItem/examples/basic-griditem.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/GridItem/examples/basic-griditem.example.ts new file mode 100644 index 0000000000..25e49ca0b4 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/GridItem/examples/basic-griditem.example.ts @@ -0,0 +1,42 @@ +import {extension, Grid, GridItem, View} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const grid = root.createComponent( + Grid, + { + columns: ['20%', 'fill', 'auto'], + rows: [300, 'auto'], + }, + [ + root.createComponent( + View, + {border: 'base', padding: 'base'}, + '20% / 300', + ), + root.createComponent( + View, + {border: 'base', padding: 'base'}, + 'fill / 300', + ), + root.createComponent( + View, + {border: 'base', padding: 'base'}, + 'auto / 300', + ), + root.createComponent(GridItem, {columnSpan: 2}, [ + root.createComponent( + View, + {border: 'base', padding: 'base'}, + '20% + fill / auto', + ), + ]), + root.createComponent( + View, + {border: 'base', padding: 'base'}, + 'auto / auto', + ), + ], + ); + + root.appendChild(grid); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Heading/Heading.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Heading/Heading.doc.ts new file mode 100644 index 0000000000..74475a799f --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Heading/Heading.doc.ts @@ -0,0 +1,69 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Heading', + description: + 'Headings control the visual style of headings. Use headings to introduce major sections, like Contact information, Shipping address, or Shipping method.\n\nUnlike HTML headings, you don’t explicitly specify the position of the heading in the document outline. Nest headings within the heading group component to control the document outline structure used by assistive technologies.', + thumbnail: 'heading-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'HeadingProps', + description: '', + type: 'HeadingProps', + }, + ], + category: 'UI components', + subCategory: 'Typography and content', + defaultExample: { + image: 'heading-default.png', + codeblock: { + title: 'Basic Heading', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Heading/examples/basic-heading.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-heading.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + '- Add a heading at the top of each section that clearly describe what’s below.\n\n- Use the heading to highlight the most important concepts or pieces of information that customers need to know.', + }, + ], + related: [ + { + name: 'HeadingGroup', + subtitle: 'Component', + url: 'headinggroup', + type: 'Component', + }, + { + name: 'Text', + subtitle: 'Component', + url: 'text', + type: 'Component', + }, + { + name: 'TextBlock', + subtitle: 'Component', + url: 'textblock', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Heading/examples/basic-heading.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Heading/examples/basic-heading.example.ts new file mode 100644 index 0000000000..1fc7da6afc --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Heading/examples/basic-heading.example.ts @@ -0,0 +1,7 @@ +import {extension, Heading} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const heading = root.createComponent(Heading, undefined, 'Store name'); + + root.appendChild(heading); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/HeadingGroup/HeadingGroup.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/HeadingGroup/HeadingGroup.doc.ts new file mode 100644 index 0000000000..9e5976c19c --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/HeadingGroup/HeadingGroup.doc.ts @@ -0,0 +1,63 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'HeadingGroup', + description: + 'Heading group controls the heading level of headings nested within it, like H1, H2, H3.\n\nUse a heading group whenever you use a heading to ensure the experience is the same for screen reader users. When using a heading, any children related to that heading should be nested within the same heading group.', + thumbnail: 'headinggroup-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [], + category: 'UI components', + subCategory: 'Typography and content', + defaultExample: { + image: 'headinggroup-default.png', + codeblock: { + title: 'Basic HeadingGroup', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/HeadingGroup/examples/basic-headinggroup.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-headinggroup.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + '- Use this component to create a content hierarchy within the document outline.', + }, + ], + related: [ + { + name: 'Heading', + subtitle: 'Component', + url: 'heading', + type: 'Component', + }, + { + name: 'Text', + subtitle: 'Component', + url: 'text', + type: 'Component', + }, + { + name: 'TextBlock', + subtitle: 'Component', + url: 'textblock', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/HeadingGroup/examples/basic-headinggroup.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/HeadingGroup/examples/basic-headinggroup.example.ts new file mode 100644 index 0000000000..d36be5ec4f --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/HeadingGroup/examples/basic-headinggroup.example.ts @@ -0,0 +1,20 @@ +import { + extension, + HeadingGroup, + Heading, + View, +} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const headingGroup = root.createComponent(View, undefined, [ + root.createComponent(Heading, undefined, 'Heading

'), + root.createComponent(HeadingGroup, undefined, [ + root.createComponent(Heading, undefined, 'Heading

'), + root.createComponent(HeadingGroup, undefined, [ + root.createComponent(Heading, undefined, 'Heading

'), + ]), + ]), + ]); + + root.appendChild(headingGroup); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Icon/Icon.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Icon/Icon.doc.ts new file mode 100644 index 0000000000..1cceef7eb7 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Icon/Icon.doc.ts @@ -0,0 +1,57 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Icon', + description: + 'Icons are pictograms or graphic symbols. They can act as wayfinding tools or as a means of communicating functionality.', + thumbnail: 'icon-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'IconProps', + description: '', + type: 'IconProps', + }, + ], + category: 'UI components', + subCategory: 'Media and visuals', + defaultExample: { + image: 'icon-default.png', + codeblock: { + title: 'Basic Icon', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Icon/examples/basic-icon.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-icon.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'icons', + title: 'Icons', + sectionContent: + '', + }, + { + type: 'Generic', + anchorLink: 'appearance', + title: 'Appearance', + sectionContent: + '| Value | Description |\n| --- | --- |\n| "accent" | Conveys emphasis and draws attention to the element. |\n| "interactive" | Conveys that the element is pressable, hoverable or otherwise interactive. |\n| "subdued" | Conveys a subdued or disabled state for the element. |\n| "info" | Conveys that the element is informative or has information. |\n| "success" | Convey a successful interaction. |\n| "warning" | Convey something needs attention or an action needs to be taken. |\n| "critical" | Conveys a problem has arisen. |\n| "monochrome" | Takes the color of its parent.|', + }, + ], + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Icon/examples/basic-icon.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Icon/examples/basic-icon.example.ts new file mode 100644 index 0000000000..9330733970 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Icon/examples/basic-icon.example.ts @@ -0,0 +1,7 @@ +import {extension, Icon} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const icon = root.createComponent(Icon, {source: 'discount'}); + + root.appendChild(icon); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Image/Image.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Image/Image.doc.ts new file mode 100644 index 0000000000..f1ee095ebd --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Image/Image.doc.ts @@ -0,0 +1,49 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Image', + description: 'Image is used for large format, responsive images.', + thumbnail: 'image-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'ImageProps', + description: '', + type: 'ImageProps', + }, + ], + category: 'UI components', + subCategory: 'Media and visuals', + defaultExample: { + image: 'image-default.png', + codeblock: { + title: 'Basic Image', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Image/examples/basic-image.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-image.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'loading', + title: 'Loading', + sectionContent: + '| Value | Description |\n| --- | --- |\n| "eager" | Image is loaded immediately, regardless of whether or not the image is currently within the visible viewport. |\n| "lazy" | Image is loaded when it’s within the visible viewport. |', + }, + ], + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Image/examples/basic-image.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Image/examples/basic-image.example.ts new file mode 100644 index 0000000000..515747665a --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Image/examples/basic-image.example.ts @@ -0,0 +1,9 @@ +import {extension, Image} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const image = root.createComponent(Image, { + source: 'https://cdn.shopify.com/YOUR_IMAGE_HERE', + }); + + root.appendChild(image); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ImageGroup/ImageGroup.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ImageGroup/ImageGroup.doc.ts index 61ec3b877c..73ea208b69 100644 --- a/packages/ui-extensions/src/surfaces/customer-account/components/ImageGroup/ImageGroup.doc.ts +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ImageGroup/ImageGroup.doc.ts @@ -15,7 +15,8 @@ const data: ReferenceEntityTemplateSchema = { type: 'ImageGroupProps', }, ], - category: 'components', + category: 'UI components', + subCategory: 'Media and visuals', defaultExample: { image: 'imagegroup-preview.png', altText: diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/InlineLayout/InlineLayout.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/InlineLayout/InlineLayout.doc.ts new file mode 100644 index 0000000000..411908fccf --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/InlineLayout/InlineLayout.doc.ts @@ -0,0 +1,68 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +import {getExample} from '../../helper.docs'; + +const disclosureAndAlignment = getExample( + 'ui-components/disclosure-and-alignment', + ['jsx', 'js'], +); + +const data: ReferenceEntityTemplateSchema = { + name: 'InlineLayout', + description: + 'InlineLayout is used to lay out content over multiple columns.\n\nBy default, all columns are of equal size and fill the available inline space. Content does not wrap on new rows when not enough columns have been explicitly set, instead they are added as new column and fill the remaining inline space.', + thumbnail: 'inlinelayout-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'InlineLayoutProps', + description: '', + type: 'InlineLayoutProps', + }, + ], + category: 'UI components', + subCategory: 'Layout and structure', + defaultExample: { + image: 'inlinelayout-default.png', + codeblock: { + title: 'Basic InlineLayout', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/InlineLayout/examples/basic-inlinelayout.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-inlinelayout.example.ts', + language: 'js', + }, + ], + }, + }, + examples: { + description: '', + examples: [disclosureAndAlignment], + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'accessibility-roles', + title: 'Accessibility roles', + sectionContent: + '| Value | Description |\n| --- | --- |\n| "main" | Used to indicate the primary content. |\n| "header" | Used to indicate the component is a header. |\n| "footer" | Used to display information such as copyright information, navigation links, and privacy statements. |\n| "section" | Used to indicate a generic section. |\n| "complementary" | Used to designate a supporting section that relates to the main content. |\n| "navigation" | Used to identify major groups of links used for navigating. |\n| "orderedList" | Used to identify a list of ordered items. |\n| "listItem" | Used to identify an item inside a list of items. |\n| "unorderedList" | Used to identify a list of unordered items. |\n| "separator" | Used to indicates the component acts as a divider that separates and distinguishes sections of content. |\n| "status" | Used to define a live region containing advisory information for the user that is not important enough to be an alert. |\n| "alert" | Used for important, and usually time-sensitive, information. |', + }, + ], + related: [ + { + subtitle: 'Utility', + name: 'StyleHelper', + url: '/docs/api/checkout-ui-extensions/unstable/components/utilities/stylehelper', + type: 'utility', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/InlineLayout/examples/basic-inlinelayout.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/InlineLayout/examples/basic-inlinelayout.example.ts new file mode 100644 index 0000000000..903b79f4fb --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/InlineLayout/examples/basic-inlinelayout.example.ts @@ -0,0 +1,16 @@ +import {extension, InlineLayout, View} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const inlineLayout = root.createComponent( + InlineLayout, + { + columns: ['20%', 'fill'], + }, + [ + root.createComponent(View, {border: 'base', padding: 'base'}, '20%'), + root.createComponent(View, {border: 'base', padding: 'base'}, 'fill'), + ], + ); + + root.appendChild(inlineLayout); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/InlineSpacer/InlineSpacer.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/InlineSpacer/InlineSpacer.doc.ts new file mode 100644 index 0000000000..b08d62fce5 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/InlineSpacer/InlineSpacer.doc.ts @@ -0,0 +1,41 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'InlineSpacer', + description: + 'InlineSpacer is used to create empty inline space, typically when variable spacing is needed between multiple elements.\n\nNote that you should favor InlineStack when spacing between all elements is the same.', + isVisualComponent: true, + thumbnail: 'inlinespacer-thumbnail.png', + requires: '', + type: '', + definitions: [ + { + title: 'InlineSpacerProps', + description: '', + type: 'InlineSpacerProps', + }, + ], + category: 'UI components', + subCategory: 'Layout and structure', + defaultExample: { + image: 'inlinespacer-default.png', + codeblock: { + title: 'Basic InlineSpacer', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/InlineSpacer/examples/basic-inlinespacer.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-inlinespacer.example.ts', + language: 'js', + }, + ], + }, + }, + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/InlineSpacer/examples/basic-inlinespacer.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/InlineSpacer/examples/basic-inlinespacer.example.ts new file mode 100644 index 0000000000..c901e9c7a2 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/InlineSpacer/examples/basic-inlinespacer.example.ts @@ -0,0 +1,20 @@ +import { + extension, + InlineSpacer, + InlineStack, + View, +} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const inlineSpacer = root.createComponent(InlineStack, {spacing: 'none'}, [ + root.createComponent(View, {border: 'base', padding: 'base'}, 'View'), + root.createComponent(InlineSpacer, {spacing: 'loose'}), + root.createComponent(View, {border: 'base', padding: 'base'}, 'View'), + root.createComponent(InlineSpacer, {spacing: 'tight'}), + root.createComponent(View, {border: 'base', padding: 'base'}, 'View'), + root.createComponent(InlineSpacer, {spacing: 'base'}), + root.createComponent(View, {border: 'base', padding: 'base'}, 'View'), + ]); + + root.appendChild(inlineSpacer); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/InlineStack/InlineStack.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/InlineStack/InlineStack.doc.ts new file mode 100644 index 0000000000..8e27a1a60b --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/InlineStack/InlineStack.doc.ts @@ -0,0 +1,57 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'InlineStack', + description: + 'InlineStack is used to lay out a horizontal row of elements. Elements always wrap.', + isVisualComponent: true, + thumbnail: 'inlinestack-thumbnail.png', + requires: '', + type: '', + definitions: [ + { + title: 'InlineStackProps', + description: '', + type: 'InlineStackProps', + }, + ], + category: 'UI components', + subCategory: 'Layout and structure', + defaultExample: { + image: 'inlinestack-default.png', + codeblock: { + title: 'Basic InlineStack', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/InlineStack/examples/basic-inlinestack.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-inlinestack.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'accessibility-roles', + title: 'Accessibility roles', + sectionContent: + '| Value | Description |\n| --- | --- |\n| "main" | Used to indicate the primary content. |\n| "header" | Used to indicate the component is a header. |\n| "footer" | Used to display information such as copyright information, navigation links, and privacy statements. |\n| "section" | Used to indicate a generic section. |\n| "complementary" | Used to designate a supporting section that relates to the main content. |\n| "navigation" | Used to identify major groups of links used for navigating. |\n| "orderedList" | Used to identify a list of ordered items. |\n| "listItem" | Used to identify an item inside a list of items. |\n| "unorderedList" | Used to identify a list of unordered items. |\n| "separator" | Used to indicates the component acts as a divider that separates and distinguishes sections of content. |\n| "status" | Used to define a live region containing advisory information for the user that is not important enough to be an alert. |\n| "alert" | Used for important, and usually time-sensitive, information. |', + }, + ], + related: [ + { + subtitle: 'Utility', + name: 'StyleHelper', + url: '/docs/api/checkout-ui-extensions/unstable/components/utilities/stylehelper', + type: 'utility', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/InlineStack/examples/basic-inlinestack.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/InlineStack/examples/basic-inlinestack.example.ts new file mode 100644 index 0000000000..19371d66c4 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/InlineStack/examples/basic-inlinestack.example.ts @@ -0,0 +1,18 @@ +import {extension, InlineStack, View} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const inlineStack = root.createComponent( + InlineStack, + { + spacing: 'base', + }, + [ + root.createComponent(View, {border: 'base', padding: 'base'}, 'View'), + root.createComponent(View, {border: 'base', padding: 'base'}, 'View'), + root.createComponent(View, {border: 'base', padding: 'base'}, 'View'), + root.createComponent(View, {border: 'base', padding: 'base'}, 'View'), + ], + ); + + root.appendChild(inlineStack); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Link/Link.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Link/Link.doc.ts new file mode 100644 index 0000000000..ab61bcb80a --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Link/Link.doc.ts @@ -0,0 +1,64 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Link', + description: + 'Link makes text interactive so customers can perform an action, such as navigating to another location.', + requires: '', + isVisualComponent: true, + thumbnail: 'link-thumbnail.png', + type: '', + definitions: [ + { + title: 'LinkProps', + description: '', + type: 'LinkProps', + }, + ], + category: 'UI components', + subCategory: 'Actions', + defaultExample: { + image: 'link-default.png', + codeblock: { + title: 'Basic Link', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Link/examples/basic-link.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-link.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'appearance', + title: 'Appearance', + sectionContent: + '| Value | Description |\n| --- | --- |\n| "monochrome" | Takes the color of its parent.|', + }, + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + '- If the link isn’t in a paragraph, then consider using a plain button instead for a larger hit area.\n\n- Use links primarily for navigation and use buttons primarily for actions.\n\n- The HTML that renders for the Button and `Link` components includes style and accessibility information. Use these components intentionally and consistently to provide a more inclusive experience for assistive technology users and a more cohesive visual experience for sighted users.', + }, + ], + related: [ + { + name: 'Button', + subtitle: 'Component', + url: 'button', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Link/examples/basic-link.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Link/examples/basic-link.example.ts new file mode 100644 index 0000000000..3f723344f1 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Link/examples/basic-link.example.ts @@ -0,0 +1,11 @@ +import {extension, Link} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const link = root.createComponent( + Link, + {to: 'https://www.shopify.ca/climate/sustainability-fund'}, + 'Sustainability fund', + ); + + root.appendChild(link); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/List/List.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/List/List.doc.ts new file mode 100644 index 0000000000..794c12e59e --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/List/List.doc.ts @@ -0,0 +1,58 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'List', + description: + 'Lists display a set of related content. Each list item usually begins with a bullet or a number.', + + requires: '', + thumbnail: 'list-thumbnail.png', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'ListProps', + description: '', + type: 'ListProps', + }, + ], + category: 'UI components', + subCategory: 'Typography and content', + defaultExample: { + image: 'list-default.png', + codeblock: { + title: 'Basic List', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/List/examples/basic-list.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-list.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + '- Use lists to break up chunks of related content to make the information easier for customers to scan.\n\n- Phrase list items consistently. Try to start each item with a noun or a verb and be consistent with each item.\n\n- Use bullets for a text-only list of related items that don’t need to be in a specific order.\n\n- Use numbers for a text-only list of related items when you need to communicate order, priority, or sequence.\n\n- Don’t use a marker when only the semantic value of a list matters, such as with a list of links.', + }, + ], + related: [ + { + name: 'ListItem', + subtitle: 'Component', + url: 'listItem', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/List/examples/basic-list.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/List/examples/basic-list.example.ts new file mode 100644 index 0000000000..3a28e41ec6 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/List/examples/basic-list.example.ts @@ -0,0 +1,11 @@ +import {extension, List, ListItem} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const list = root.createComponent(List, undefined, [ + root.createComponent(ListItem, undefined, '100% organic cotton'), + root.createComponent(ListItem, undefined, 'Made in Canada'), + root.createComponent(ListItem, undefined, 'Machine washable'), + ]); + + root.appendChild(list); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ListItem/ListItem.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ListItem/ListItem.doc.ts new file mode 100644 index 0000000000..0519f82686 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ListItem/ListItem.doc.ts @@ -0,0 +1,42 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'ListItem', + description: + 'List items are used as children of the `List` component.\n\nThey usually begins with a bullet or a number.', + requires: '', + thumbnail: 'listitem-thumbnail.png', + isVisualComponent: true, + type: '', + definitions: [], + category: 'UI components', + subCategory: 'Typography and content', + defaultExample: { + image: 'listitem-default.png', + codeblock: { + title: 'Basic ListItem', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/ListItem/examples/basic-listitem.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-listitem.example.ts', + language: 'js', + }, + ], + }, + }, + related: [ + { + name: 'List', + subtitle: 'Component', + url: 'list', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ListItem/examples/basic-listitem.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ListItem/examples/basic-listitem.example.ts new file mode 100644 index 0000000000..28d8a96f6e --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ListItem/examples/basic-listitem.example.ts @@ -0,0 +1,9 @@ +import {extension, List, ListItem} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const list = root.createComponent(List, undefined, [ + root.createComponent(ListItem, undefined, '100% organic cotton'), + ]); + + root.appendChild(list); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Map/Map.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Map/Map.doc.ts new file mode 100644 index 0000000000..bcd066554e --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Map/Map.doc.ts @@ -0,0 +1,54 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Map', + description: + 'Use the Map component to provide visual representation of geographic data such as verifying address, package or pickup locations.\n\nPlease note that the merchant or partner has to provide an API key and a set of allowed domains where the map would render.\n\nThe 3 necessary domains needed are:\n\n- `https://*.[MERCHANT-DOMAIN].com`\n\n- `https://shop.app`\n\n- `https://shopify.com` \n\n Where `*` is a wildcard. Learn more about [match patterns](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Match_patterns).\n\n Please refer to the [Google Maps Platform documentation](https://developers.google.com/maps/documentation/javascript/get-api-key) for more details on how to get an API key.', + requires: '', + thumbnail: 'map-thumbnail.png', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'MapProps', + description: '', + type: 'MapProps', + }, + ], + category: 'UI components', + subCategory: 'Media and visuals', + defaultExample: { + image: 'map-default.png', + codeblock: { + title: 'Basic Map', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Map/examples/basic-map.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-map.example.ts', + language: 'js', + }, + ], + }, + }, + related: [ + { + name: 'MapMarker', + subtitle: 'Component', + url: 'mapmarker', + type: 'Component', + }, + { + name: 'MapPopover', + subtitle: 'Component', + url: 'mappopover', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Map/examples/basic-map.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Map/examples/basic-map.example.ts new file mode 100644 index 0000000000..7b90b1a1cd --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Map/examples/basic-map.example.ts @@ -0,0 +1,13 @@ +import {extension, Map} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const map = root.createComponent(Map, { + apiKey: 'YOUR_API_KEY', + accessibilityLabel: 'Map showing pickup locations', + latitude: -28.024, + longitude: 140.887, + zoom: 4, + }); + + root.appendChild(map); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/MapMarker/MapMarker.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/MapMarker/MapMarker.doc.ts new file mode 100644 index 0000000000..7f648d32e7 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/MapMarker/MapMarker.doc.ts @@ -0,0 +1,62 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'MapMarker', + description: + 'MapMarker represents a specific location or point of interest on a map.', + requires: '', + thumbnail: 'mapmarker-thumbnail.png', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'MapMarkerProps', + description: '', + type: 'MapMarkerProps', + }, + ], + category: 'UI components', + subCategory: 'Media and visuals', + defaultExample: { + image: 'mapmarker-default.png', + codeblock: { + title: 'Basic MapMarker', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/MapMarker/examples/basic-mapmarker.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-mapmarker.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: `- If your markers are interactive, make sure that the selected marker's icon is different from the rest of the non-selected markers.\n\n- If there are a large number of markers obscuring important features of the map, set the markers to clusterable to help increase the readability of the map.`, + }, + ], + related: [ + { + name: 'Map', + subtitle: 'Component', + url: 'map', + type: 'Component', + }, + { + name: 'MapPopover', + subtitle: 'Component', + url: 'mappopover', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/MapMarker/examples/basic-mapmarker.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/MapMarker/examples/basic-mapmarker.example.ts new file mode 100644 index 0000000000..dae5d1d4ca --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/MapMarker/examples/basic-mapmarker.example.ts @@ -0,0 +1,23 @@ +import {extension, Map, MapMarker} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const map = root.createComponent( + Map, + { + apiKey: 'YOUR_API_KEY', + accessibilityLabel: 'Map', + latitude: -28.024, + longitude: 140.887, + zoom: 4, + }, + [ + root.createComponent(MapMarker, { + latitude: -28.024, + longitude: 140.887, + accessibilityLabel: 'Map marker for Innamincka, Australia', + }), + ], + ); + + root.appendChild(map); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/MapPopover/MapPopover.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/MapPopover/MapPopover.doc.ts new file mode 100644 index 0000000000..3609a287df --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/MapPopover/MapPopover.doc.ts @@ -0,0 +1,62 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'MapPopover', + description: + 'MapPopover provides additional information or context about a specific location or point of interest on a map.', + requires: '', + thumbnail: 'mappopover-thumbnail.png', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'MapPopoverProps', + description: '', + type: 'MapPopoverProps', + }, + ], + category: 'UI components', + subCategory: 'Media and visuals', + defaultExample: { + image: 'mappopover-default.png', + codeblock: { + title: 'Basic MapPopover', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/MapPopover/examples/basic-mappopover.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-mappopover.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: `- Use to display relevant details such as the name, address, description, or other pertinent information related to the location.\n\n- Ensure that the content displayed in the map popover is brief, relevant, and easy to understand.\n\n- Maintain visual consistency with the overall design of the checkout. `, + }, + ], + related: [ + { + name: 'Map', + subtitle: 'Component', + url: 'map', + type: 'Component', + }, + { + name: 'MapMarker', + subtitle: 'Component', + url: 'mapmarker', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/MapPopover/examples/basic-mappopover.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/MapPopover/examples/basic-mappopover.example.ts new file mode 100644 index 0000000000..42f238effb --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/MapPopover/examples/basic-mappopover.example.ts @@ -0,0 +1,36 @@ +import { + extension, + Map, + MapMarker, + MapPopover, +} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const popoverFragment = root.createFragment(); + const popover = root.createComponent( + MapPopover, + {}, + 'Blue Mountains National Park store', + ); + popoverFragment.appendChild(popover); + const map = root.createComponent( + Map, + { + apiKey: 'YOUR_API_KEY', + accessibilityLabel: 'Map', + latitude: -28.024, + longitude: 140.887, + zoom: 4, + }, + [ + root.createComponent(MapMarker, { + latitude: -28.024, + longitude: 140.887, + accessibilityLabel: 'Map marker for Innamincka, Australia', + overlay: popoverFragment, + }), + ], + ); + + root.appendChild(map); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Menu/Menu.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Menu/Menu.doc.ts index 09a2356db4..2a3be1108e 100644 --- a/packages/ui-extensions/src/surfaces/customer-account/components/Menu/Menu.doc.ts +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Menu/Menu.doc.ts @@ -22,7 +22,8 @@ const data: ReferenceEntityTemplateSchema = { type: 'Docs_Menu_Button_Action', }, ], - category: 'components', + category: 'UI components', + subCategory: 'Actions', defaultExample: { image: 'menu-default.png', altText: 'An example of a Menu with three actions.', diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Modal/Modal.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Modal/Modal.doc.ts new file mode 100644 index 0000000000..116c5b01d8 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Modal/Modal.doc.ts @@ -0,0 +1,57 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Modal', + description: + 'Modals are a special type of overlay that shift focus towards a specific action/set of information before the main flow can proceed. They must be specified inside the `overlay` prop of an activator component (`Button`, `Link` or `Pressable`).\n\nThe library automatically applies the [WAI-ARIA Dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/) to both the activator and the modal content.', + requires: '', + thumbnail: 'modal-thumbnail.png', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'ModalProps', + description: '', + type: 'ModalProps', + }, + ], + category: 'UI components', + subCategory: 'Overlays', + defaultExample: { + image: 'modal-default.png', + codeblock: { + title: 'Basic Modal', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Modal/examples/basic-modal.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-modal.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + 'Use modals if:\n\n- The information needed to be shown is not critical in completing the checkout process and information cannot be condensed into one sentence.\n\n- The information the buyer is entering requires less than two rows of input fields.\n\n- The information the buyer is entering is not reliant on information on the page (which is underneath the modal and not visible to them).', + }, + ], + related: [ + { + name: 'Ui', + subtitle: 'API', + url: 'ui', + type: 'API', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Modal/examples/basic-modal.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Modal/examples/basic-modal.example.ts new file mode 100644 index 0000000000..9074774ff1 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Modal/examples/basic-modal.example.ts @@ -0,0 +1,44 @@ +import { + extension, + Button, + Link, + Modal, + TextBlock, +} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root, {ui}) => { + const modalFragment = root.createFragment(); + const modal = root.createComponent( + Modal, + {id: 'my-modal', title: 'Return policy', padding: true}, + [ + root.createComponent( + TextBlock, + undefined, + 'We have a 30-day return policy, which means you have 30 days after receiving your item to request a return.', + ), + root.createComponent( + TextBlock, + undefined, + 'To be eligible for a return, your item must be in the same condition that you received it, unworn or unused, with tags, and in its original packaging. You’ll also need the receipt or proof of purchase.', + ), + root.createComponent( + Button, + { + onPress() { + ui.overlay.close('my-modal'); + }, + }, + 'Close', + ), + ], + ); + modalFragment.appendChild(modal); + const link = root.createComponent( + Link, + {overlay: modalFragment}, + 'Return policy', + ); + + root.appendChild(link); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Page/Page.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Page/Page.doc.ts index 1a82081f62..a56964b358 100644 --- a/packages/ui-extensions/src/surfaces/customer-account/components/Page/Page.doc.ts +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Page/Page.doc.ts @@ -27,7 +27,8 @@ const data: ReferenceEntityTemplateSchema = { type: 'Docs_Page_Button_SecondaryAction', }, ], - category: 'components', + category: 'UI components', + subCategory: 'Layout and structure', defaultExample: { image: 'page-preview.png', altText: diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/PaymentIcon/PaymentIcon.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/PaymentIcon/PaymentIcon.doc.ts new file mode 100644 index 0000000000..61125c6c24 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/PaymentIcon/PaymentIcon.doc.ts @@ -0,0 +1,55 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'PaymentIcon', + description: + 'Payment icons can be used for displaying payment-related information or features such as a user’s saved or available payment methods.', + thumbnail: 'paymenticon-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'PaymentIconProps', + description: '', + type: 'PaymentIconProps', + }, + { + title: 'PaymentMethod', + description: '', + type: 'PaymentMethod', + }, + ], + category: 'UI components', + subCategory: 'Media and visuals', + defaultExample: { + image: 'paymenticon-default.png', + codeblock: { + title: 'Basic PaymentIcon', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/PaymentIcon/examples/basic-paymenticon.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-paymenticon.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + '- Maintain the interior appearance of the SVG. The branded portion of the payment icon as provided meets the brand guidelines of the payment provider.\n\n- Maintain the border property of the payment icon. It is designed to adapt to merchant branding in Checkout and ensures a consistent appearance across the customer experience.\n\n- The icon size is designed to be displayed consistently across checkout.', + }, + ], + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/PaymentIcon/examples/basic-paymenticon.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/PaymentIcon/examples/basic-paymenticon.example.ts new file mode 100644 index 0000000000..2643bdfee8 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/PaymentIcon/examples/basic-paymenticon.example.ts @@ -0,0 +1,7 @@ +import {extension, PaymentIcon} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const paymentIcon = root.createComponent(PaymentIcon, {name: 'shop-pay'}); + + root.appendChild(paymentIcon); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/PhoneField/PhoneField.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/PhoneField/PhoneField.doc.ts new file mode 100644 index 0000000000..b6b1b21c41 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/PhoneField/PhoneField.doc.ts @@ -0,0 +1,41 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'PhoneField', + description: + 'A PhoneField is an input field that merchants can type into optimized for phone numbers with a country code base auto-formatting. The country code is required for the initial render of the field but it can be overriden later by the user either by selecting a country in the country selection dropdown or by manually editing the country phone code directly in the text field.', + requires: '', + isVisualComponent: true, + thumbnail: 'phonefield-thumbnail.png', + type: '', + definitions: [ + { + title: 'PhoneFieldProps', + description: '', + type: 'PhoneFieldProps', + }, + ], + category: 'UI components', + subCategory: 'Forms', + defaultExample: { + image: 'phonefield-default.png', + codeblock: { + title: 'Basic PhoneField', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/PhoneField/examples/basic-phonefield.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-phonefield.example.ts', + language: 'js', + }, + ], + }, + }, + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/PhoneField/examples/basic-phonefield.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/PhoneField/examples/basic-phonefield.example.ts new file mode 100644 index 0000000000..6ac80d2c21 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/PhoneField/examples/basic-phonefield.example.ts @@ -0,0 +1,10 @@ +import {extension, PhoneField} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const phoneField = root.createComponent(PhoneField, { + label: 'Phone', + value: '1 (555) 555-5555', + }); + + root.appendChild(phoneField); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Popover/Popover.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Popover/Popover.doc.ts new file mode 100644 index 0000000000..cc74332c28 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Popover/Popover.doc.ts @@ -0,0 +1,57 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Popover', + description: + 'Popovers are similar to tooltips. They are small overlays that open on demand after a user interaction. The difference is that the popover can contain more content, without cluttering the page. They must be specified inside the `overlay` prop of an activator component (`Button`, `Link` or `Pressable`).\n\nThe library automatically applies the WAI-ARIA Popover Widget pattern to both the activator and the popover content.', + requires: '', + isVisualComponent: true, + thumbnail: 'popover-thumbnail.png', + type: '', + definitions: [ + { + title: 'PopoverProps', + description: '', + type: 'PopoverProps', + }, + ], + category: 'UI components', + subCategory: 'Overlays', + defaultExample: { + image: 'popover-default.png', + codeblock: { + title: 'Basic Popover', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Popover/examples/basic-popover.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-popover.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + 'Use popovers if:\n\n- The intent is to ask the customer for information.\n\n- It’s possible to use at most two rows of input fields to get the information.', + }, + ], + related: [ + { + name: 'Ui', + subtitle: 'API', + url: 'ui', + type: 'API', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Popover/examples/basic-popover.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Popover/examples/basic-popover.example.ts new file mode 100644 index 0000000000..cb4657fbab --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Popover/examples/basic-popover.example.ts @@ -0,0 +1,30 @@ +import { + extension, + Pressable, + Popover, + View, + TextBlock, +} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const popoverFragment = root.createFragment(); + const popover = root.createComponent(Popover, {}, [ + root.createComponent(View, {maxInlineSize: 200, padding: 'base'}, [ + root.createComponent(TextBlock, {}, 'A thoughtful way to pay'), + root.createComponent(TextBlock, {}, 'Tap don’t type'), + root.createComponent( + TextBlock, + {}, + 'Shop Pay remembers your important details, so you can fill carts, not forms. And everything is encrypted so you can speed safely through checkout.', + ), + ]), + ]); + popoverFragment.appendChild(popover); + const pressable = root.createComponent( + Pressable, + {overlay: popoverFragment}, + 'More info', + ); + + root.appendChild(pressable); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Pressable/Pressable.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Pressable/Pressable.doc.ts new file mode 100644 index 0000000000..54921145fe --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Pressable/Pressable.doc.ts @@ -0,0 +1,48 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Pressable', + description: + 'Pressable is a generic interactive component. It shares the same styling properties as View but also adds pressable behavior, meaning that you can execute some logic in response to user interaction. Use this component for creating interactive elements without the default styling that comes with `Button` and `Link`.', + requires: '', + thumbnail: 'pressable-thumbnail.png', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'PressableProps', + description: '', + type: 'PressableProps', + }, + ], + category: 'UI components', + subCategory: 'Actions', + defaultExample: { + image: 'pressable-default.png', + codeblock: { + title: 'Basic Pressable', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Pressable/examples/basic-pressable.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-pressable.example.ts', + language: 'js', + }, + ], + }, + }, + related: [ + { + subtitle: 'Utility', + name: 'StyleHelper', + url: '/docs/api/checkout-ui-extensions/unstable/components/utilities/stylehelper', + type: 'utility', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Pressable/examples/basic-pressable.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Pressable/examples/basic-pressable.example.ts new file mode 100644 index 0000000000..8f99d2017a --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Pressable/examples/basic-pressable.example.ts @@ -0,0 +1,27 @@ +import { + extension, + Icon, + InlineLayout, + Pressable, + Text, +} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const pressable = root.createComponent( + Pressable, + { + border: 'base', + cornerRadius: 'base', + padding: 'base', + onPress: () => console.log('onPress event'), + }, + [ + root.createComponent(InlineLayout, {columns: ['fill', 'auto']}, [ + root.createComponent(Text, {}, 'Details'), + root.createComponent(Icon, {source: 'chevronDown', size: 'small'}), + ]), + ], + ); + + root.appendChild(pressable); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ProductThumbnail/ProductThumbnail.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ProductThumbnail/ProductThumbnail.doc.ts new file mode 100644 index 0000000000..2b3a13fa66 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ProductThumbnail/ProductThumbnail.doc.ts @@ -0,0 +1,50 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'ProductThumbnail', + description: + 'Product thumbnail is a representation of a product image. It provides a visual preview of the item, so buyers can quickly identify products.', + thumbnail: 'productthumbnail-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'ProductThumbnailProps', + description: '', + type: 'ProductThumbnailProps', + }, + ], + category: 'UI components', + subCategory: 'Media and visuals', + defaultExample: { + image: 'productthumbnail-default.png', + codeblock: { + title: 'Basic ProductThumbnail', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/ProductThumbnail/examples/basic-productthumbnail.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-productthumbnail.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + '**High-quality images and consistent aspect ratio**\n\n- Use optimized product images that ensure visual clarity and loading speed. Maintain a consistent aspect ratio for product thumbnails to avoid distortion or stretching of the images.\n\n**Consistent visual style and appropriate sizes**\n\n- Keep a consistent visual style for product thumbnails throughout your store. Use appropriate size for product thumbnails depending on the layout and use case. This consistency helps buyers recognize and associate the thumbnails with your product offerings.\n\n**Accessibility considerations**\n\n- Ensure product thumbnails are accessible with descriptive alternative text (alt text).', + }, + ], + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ProductThumbnail/examples/basic-productthumbnail.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ProductThumbnail/examples/basic-productthumbnail.example.ts new file mode 100644 index 0000000000..72007cab4b --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ProductThumbnail/examples/basic-productthumbnail.example.ts @@ -0,0 +1,11 @@ +import {extension, ProductThumbnail} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const paymentIcon = root.createComponent(ProductThumbnail, { + source: + 'https://shopify.dev/assets/api/checkout-extensions/checkout/components/product-thumbnail-example-code.png', + badge: 2, + }); + + root.appendChild(paymentIcon); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Progress/Progress.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Progress/Progress.doc.ts new file mode 100644 index 0000000000..e8e1f41d43 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Progress/Progress.doc.ts @@ -0,0 +1,89 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +import {getExample} from '../../helper.docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Progress', + description: 'Use to visually represent the completion of a task or process.', + requires: '', + isVisualComponent: true, + thumbnail: 'progress-thumbnail.png', + type: '', + definitions: [ + { + title: 'ProgressProps', + description: '', + type: 'ProgressProps', + }, + ], + category: 'UI components', + subCategory: 'Feedback and status indicators', + defaultExample: { + image: 'progress-indeterminate.gif', + codeblock: { + title: 'Indeterminate state', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Progress/examples/basic-progress.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-progress.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: ` +Use components like [TextBlock](../titles-and-text/textblock) or [Text](../titles-and-text/text), along with the Progress component, to display text indicating the status of the progress bar. + +### Loading states + +For loading states, add text to reassure the user that the progress bar is not frozen. + +![A progress bar with "Loading" text](/assets/templated-apis-screenshots/checkout-ui-extensions/unstable/progress-loading.png) + +### Error states + +For error states, add text or a [Banner](./banner) to describe the error and next steps. Use the \`critical\` tone property to convey urgency. + +![A progress bar with error text that says "There was a problem with the file upload. Please try again."](/assets/templated-apis-screenshots/checkout-ui-extensions/unstable/progress-error.png) + +### Visualize a goal + +Use the Progress component to visualize a goal that's valuable to the customer. + +Here's an example of using a progress bar to show a customer's progress toward the next rewards tier: + +![A progress bar in customer accounts, showing that the customer is on their way to reaching the Botanical maven rewards tier.](/assets/templated-apis-screenshots/checkout-ui-extensions/unstable/progress-goal.png) + +Here's an example of using a progress bar to show how much more a customer needs to spend to get free shipping: + +![A progress bar at checkout, showing that the customer is $43 away from free shipping.](/assets/templated-apis-screenshots/checkout-ui-extensions/unstable/progress-free-shipping.png) + `, + }, + ], + examples: { + description: '', + examples: [ + getExample('ui-components/progress-determinate-state', ['jsx', 'js']), + ], + }, + related: [ + { + name: 'Spinner', + subtitle: 'Component', + url: 'spinner', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Progress/examples/basic-progress.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Progress/examples/basic-progress.example.ts new file mode 100644 index 0000000000..28c38d7604 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Progress/examples/basic-progress.example.ts @@ -0,0 +1,9 @@ +import {extension, Progress} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const baseProgress = root.createComponent(Progress, { + accessibilityLabel: 'Loading', + }); + + root.appendChild(baseProgress); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/QRCode/QRCode.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/QRCode/QRCode.doc.ts new file mode 100644 index 0000000000..9a3dcb5b4a --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/QRCode/QRCode.doc.ts @@ -0,0 +1,71 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +import {getExample} from '../../helper.docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'QRCode', + description: 'Used to quickly access scannable data.', + requires: '', + isVisualComponent: true, + thumbnail: 'qrcode-thumbnail.png', + type: '', + definitions: [ + { + title: 'QRCodeProps', + description: '', + type: 'QRCodeProps', + }, + ], + category: 'UI components', + subCategory: 'Media and visuals', + defaultExample: { + image: 'qrcode-default.png', + codeblock: { + title: 'Basic QR Code', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/QRCode/examples/basic-qrcode.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-qrcode.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: ` +- Always test that the QR code is scannable from a smartphone. +- Include a square logo if that’s what your customers are familiar with. +- Increase usability by adding a text description of what the QR code does. +- Always provide an alternate method for customers to access the content of the QR code. + - If the content is a URL, provide a [\`Link\`](/docs/api/checkout-ui-extensions/components/link) nearby. + - If the content is data, provide a [\`Button\`](/docs/api/checkout-ui-extensions/components/button) to copy the data to the clipboard, or show the data in a readonly [\`TextField\`](/docs/api/checkout-ui-extensions/components/textfield).`, + }, + ], + examples: { + description: '', + examples: [ + getExample('ui-components/qrcode-image', ['jsx', 'js']), + getExample('ui-components/qrcode-fill-size', ['jsx', 'js']), + getExample('ui-components/clipboarditem-qrcode', ['jsx', 'js']), + ], + }, + related: [ + { + name: 'ClipboardItem', + subtitle: 'Component', + url: 'clipboarditem', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/QRCode/examples/basic-qrcode.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/QRCode/examples/basic-qrcode.example.ts new file mode 100644 index 0000000000..ac2268fb94 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/QRCode/examples/basic-qrcode.example.ts @@ -0,0 +1,20 @@ +import { + extension, + Link, + QRCode, + TextBlock, +} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const qrCode = root.createComponent(QRCode, { + content: 'https://shopify.com', + }); + + const textBlock = root.createComponent(TextBlock, null, [ + 'Scan to visit ', + root.createComponent(Link, {to: 'https://shopify.com'}, 'Shopify.com'), + ]); + + root.appendChild(qrCode); + root.appendChild(textBlock); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ResourceItem/ResourceItem.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ResourceItem/ResourceItem.doc.ts index 304a1632cc..e6ed951833 100644 --- a/packages/ui-extensions/src/surfaces/customer-account/components/ResourceItem/ResourceItem.doc.ts +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ResourceItem/ResourceItem.doc.ts @@ -21,7 +21,8 @@ const data: ReferenceEntityTemplateSchema = { type: 'Docs_ResourceItem_Button_Action', }, ], - category: 'components', + category: 'UI components', + subCategory: 'Actions', defaultExample: { image: 'resourceitem-preview.png', altText: diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ScrollView/ScrollView.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ScrollView/ScrollView.doc.ts new file mode 100644 index 0000000000..d6d18b96bd --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ScrollView/ScrollView.doc.ts @@ -0,0 +1,48 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'ScrollView', + description: + 'ScrollView is a container for long form content, such as order summary line items, that allows for scrolling so customers can expose more content as they view.', + requires: '', + isVisualComponent: true, + thumbnail: 'scrollview-thumbnail.png', + type: '', + definitions: [ + { + title: 'ScrollViewProps', + description: '', + type: 'ScrollViewProps', + }, + ], + category: 'UI components', + subCategory: 'Layout and structure', + defaultExample: { + image: 'scrollview-default.png', + codeblock: { + title: 'Basic Scrollview', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/ScrollView/examples/basic-scrollview.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-scrollview.example.ts', + language: 'js', + }, + ], + }, + }, + related: [ + { + subtitle: 'Utility', + name: 'StyleHelper', + url: '/docs/api/checkout-ui-extensions/unstable/components/utilities/stylehelper', + type: 'utility', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ScrollView/examples/basic-scrollview.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ScrollView/examples/basic-scrollview.example.ts new file mode 100644 index 0000000000..98c4b61c4c --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ScrollView/examples/basic-scrollview.example.ts @@ -0,0 +1,38 @@ +import {extension, ScrollView, View} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const scrollView = root.createComponent(ScrollView, {maxBlockSize: 400}, [ + root.createComponent( + View, + {border: 'base', padding: 'base', minBlockSize: 50}, + 'View', + ), + root.createComponent( + View, + {border: 'base', padding: 'base', minBlockSize: 50}, + 'View', + ), + root.createComponent( + View, + {border: 'base', padding: 'base', minBlockSize: 50}, + 'View', + ), + root.createComponent( + View, + {border: 'base', padding: 'base', minBlockSize: 50}, + 'View', + ), + root.createComponent( + View, + {border: 'base', padding: 'base', minBlockSize: 50}, + 'View', + ), + root.createComponent( + View, + {border: 'base', padding: 'base', minBlockSize: 50}, + 'View', + ), + ]); + + root.appendChild(scrollView); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Select/Select.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Select/Select.doc.ts new file mode 100644 index 0000000000..d341d9df54 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Select/Select.doc.ts @@ -0,0 +1,76 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Select', + description: + 'Selects let buyers choose one option from an options menu. Consider select when you have 4 or more options, to avoid cluttering the interface.', + requires: '', + thumbnail: 'select-thumbnail.png', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'SelectProps', + description: '', + type: 'SelectProps', + }, + ], + category: 'UI components', + subCategory: 'Forms', + defaultExample: { + image: 'select-default.png', + codeblock: { + title: 'Basic Select', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Select/examples/basic-select.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-select.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + '- Use this component when customers need to choose between four or more predefined options.\n\n- Have a default option selected whenever possible. Use Select as placeholder text if there’s no logical default option.', + }, + ], + examples: { + description: '', + examples: [ + { + image: 'select-time-picking.png', + description: + 'The Select component is a great choice for displaying a long list of time choices, as it helps conserve valuable space. If the number of options is less than or equal to 5, we recommend using the [ChoiceList](/docs/api/checkout-ui-extensions/components/forms/choicelist) component. This allows buyers to see all options immediately without the need for clicking into the select.', + codeblock: { + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Select/examples/time-picking-select.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/time-picking-select.example.ts', + language: 'js', + }, + ], + title: + 'Using the Select component to display a long list of time choices', + }, + }, + ], + }, + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Select/examples/basic-select.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Select/examples/basic-select.example.ts new file mode 100644 index 0000000000..8efc62a811 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Select/examples/basic-select.example.ts @@ -0,0 +1,36 @@ +import {extension, Select} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const select = root.createComponent(Select, { + label: 'Country', + value: '2', + options: [ + { + value: '1', + label: 'Australia', + }, + { + value: '2', + label: 'Canada', + }, + { + value: '3', + label: 'France', + }, + { + value: '4', + label: 'Japan', + }, + { + value: '5', + label: 'Nigeria', + }, + { + value: '6', + label: 'United States', + }, + ], + }); + + root.appendChild(select); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Select/examples/time-picking-select.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Select/examples/time-picking-select.example.ts new file mode 100644 index 0000000000..9dffa4107a --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Select/examples/time-picking-select.example.ts @@ -0,0 +1,36 @@ +import {extension, Select} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const select = root.createComponent(Select, { + label: 'Pickup time', + value: '1', + options: [ + { + value: '1', + label: '9:00 AM', + }, + { + value: '2', + label: '9:30 AM', + }, + { + value: '3', + label: '10:00 AM', + }, + { + value: '4', + label: '10:30 AM', + }, + { + value: '5', + label: '11:00 AM', + }, + { + value: '6', + label: '11:30 AM', + }, + ], + }); + + root.appendChild(select); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Sheet/Sheet.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Sheet/Sheet.doc.ts new file mode 100644 index 0000000000..a5e4c5338f --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Sheet/Sheet.doc.ts @@ -0,0 +1,76 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +import {getExample} from '../../helper.docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Sheet', + description: + 'The Sheet component displays essential information for customers at the bottom of the screen, appearing above other elements. Use it sparingly to avoid distracting customers during checkout. This component requires access to [Customer Privacy API](/docs/api/checkout-ui-extensions/unstable/configuration#collect-buyer-consent) to be rendered. \n\nThe library automatically applies the [WAI-ARIA Dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/) to both the activator and the sheet content.', + requires: + 'configuration of the [Customer Privacy](/docs/api/checkout-ui-extensions/unstable/configuration#collect-buyer-consent) capability to be rendered.', + thumbnail: 'sheet-thumbnail.png', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'SheetProps', + description: '', + type: 'SheetProps', + }, + ], + category: 'UI components', + subCategory: 'Overlays', + defaultExample: { + image: 'sheet-default.png', + codeblock: { + title: 'Basic Sheet', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Sheet/examples/basic-sheet.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-sheet.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'shopify-controlled-surfaces', + title: 'Shopify-controlled surfaces', + sectionContent: + 'To prevent disruptions during checkout, we maintain strict design control over key areas of the Sheet component. These Shopify-controlled elements include: \n\n

Locations of elements

\n\nThe Sheet elements (header, content, action buttons, and dismiss button) are strategically positioned and sized to present vital information upfront.\n\n\n\n\n\n
\n\n

Padding and spacing

\n\n\n\n
\n\n

Maximum height

\n\nTo balance customer attention and task completion, a maximum height is set for the Sheet component.\n\n\n\n\n\nWhen content pushes the sheet to exceed this limit, the following UI behaviors are triggered:\n\n
\n\n

Heading and content are scrollable

\n\n\n\n
\n\n

Expand pill appears to allow customers to view the entire content

\n\n\n\n
\n\n

Actions slot and dismiss button remain fixed

\n\n', + }, + { + type: 'Generic', + anchorLink: 'privacy-consent-requirements', + title: 'Privacy consent requirements', + sectionContent: + '

Content

\n\nFor the best customer experience, ensure content is brief and to the point.\n\n\n\nVarious strategies can be employed to avoid content scrolling.\n\n
\n\n

Use short content

\n\n\n\n
\n\n

Use small text size

\n\n \n\n
\n\n

Remove the header

\n\n \n\n
\n\n

Actions slot

\n\nThe actions slots allows customers to make decisions and is split into primary and secondary sections.\n\n\n\n
\n\n

Primary section

\n\n Contains primary actions for customer decisions on the sheet’s prompt. Up to two buttons are allowed. Keep the button’s content brief so that it doesn’t wrap to more than one line.\n\n\n\n
\n\n

Secondary section

\n\nContains action that is unrelated to the sheet’s prompt. Only one button is allowed. A modal can be activated when engaging with the secondary action. Keep the button’s content brief so that it doesn’t wrap to more than one line.\n\n\n\n
\n\n

Consent, denial of consent, and sheet dismissal

\n\n

Consent

\n\nWhen a customer expresses consent by pressing the acceptance button, cookies will load and the sheet should not re-appear on refresh.\n\n
\n\n

Denial of consent

\n\nWhen a customer expresses denial of consent by pressing the rejection button, cookies will not load and the sheet will not re-appear on refresh.\n\n
\n\n

Sheet dismissal

\n\nWhen a customer neither grants nor denies consent by pressing the dismiss button, cookies will not load and the sheet will re-appear on refresh.\n\n', + }, + ], + examples: { + description: '', + examples: [ + getExample('ui-components/sheet-consent', ['jsx', 'js']), + getExample('ui-components/sheet-description-preferences', ['jsx', 'js']), + getExample('ui-components/sheet-icon-button-preferences', ['jsx', 'js']), + getExample('ui-components/sheet-layout-content', ['jsx', 'js']), + ], + }, + related: [ + { + name: 'Ui', + subtitle: 'API', + url: 'ui', + type: 'API', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Sheet/examples/basic-sheet.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Sheet/examples/basic-sheet.example.ts new file mode 100644 index 0000000000..be863e0d13 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Sheet/examples/basic-sheet.example.ts @@ -0,0 +1,29 @@ +import { + extension, + Link, + Sheet, + TextBlock, +} from '@shopify/ui-extensions/customer-account'; + +// This component requires access to Customer Privacy API to be rendered. + +export default extension('customer-account.page.render', (root) => { + const sheetFragment = root.createFragment(); + const sheet = root.createComponent( + Sheet, + { + id: 'basic-sheet', + heading: 'Basic Sheet', + accessibilityLabel: 'A sheet with text content', + }, + [root.createComponent(TextBlock, undefined, 'Basic Sheet Content')], + ); + sheetFragment.appendChild(sheet); + const link = root.createComponent( + Link, + {overlay: sheetFragment}, + 'Open sheet', + ); + + root.appendChild(link); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/SkeletonImage/SkeletonImage.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/SkeletonImage/SkeletonImage.doc.ts new file mode 100644 index 0000000000..720e684789 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/SkeletonImage/SkeletonImage.doc.ts @@ -0,0 +1,52 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +import {getExample} from '../../helper.docs'; + +const loadingSkeletons = getExample('ui-components/loading-skeletons', [ + 'jsx', + 'js', +]); + +const data: ReferenceEntityTemplateSchema = { + name: 'SkeletonImage', + description: + 'SkeletonImage is used to provide a low fidelity representation of an image before it appears on the page.', + requires: '', + thumbnail: 'skeletonimage-thumbnail.png', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'SkeletonImageProps', + description: '', + type: 'SkeletonImageProps', + }, + ], + category: 'UI components', + subCategory: 'Media and visuals', + defaultExample: { + image: 'skeletonimage-default.png', + codeblock: { + title: 'Basic SkeletonImage', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/SkeletonImage/examples/basic-skeletonimage.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-skeletonimage.example.ts', + language: 'js', + }, + ], + }, + }, + examples: { + description: '', + examples: [loadingSkeletons], + }, + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/SkeletonImage/examples/basic-skeletonimage.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/SkeletonImage/examples/basic-skeletonimage.example.ts new file mode 100644 index 0000000000..f5536f764b --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/SkeletonImage/examples/basic-skeletonimage.example.ts @@ -0,0 +1,10 @@ +import {extension, SkeletonImage} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const skeletonImage = root.createComponent(SkeletonImage, { + inlineSize: 300, + blockSize: 300, + }); + + root.appendChild(skeletonImage); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/SkeletonText/SkeletonText.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/SkeletonText/SkeletonText.doc.ts new file mode 100644 index 0000000000..4e66ec7738 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/SkeletonText/SkeletonText.doc.ts @@ -0,0 +1,52 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +import {getExample} from '../../helper.docs'; + +const loadingSkeletons = getExample('ui-components/loading-skeletons', [ + 'jsx', + 'js', +]); + +const data: ReferenceEntityTemplateSchema = { + name: 'SkeletonText', + description: + 'SkeletonText is used to provide a low fidelity representation of text content before it appears on the page. \n\nOptionally you can use any text content inside `SkeletonText` to be used as a base for the rendered skeleton', + requires: '', + isVisualComponent: true, + thumbnail: 'skeletontext-thumbnail.png', + type: '', + definitions: [ + { + title: 'SkeletonTextProps', + description: '', + type: 'SkeletonTextProps', + }, + ], + category: 'UI components', + subCategory: 'Typography and content', + defaultExample: { + image: 'skeletontext-default.png', + codeblock: { + title: 'Basic SkeletonText', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/SkeletonText/examples/basic-skeletontext.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-skeletontext.example.ts', + language: 'js', + }, + ], + }, + }, + examples: { + description: '', + examples: [loadingSkeletons], + }, + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/SkeletonText/examples/basic-skeletontext.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/SkeletonText/examples/basic-skeletontext.example.ts new file mode 100644 index 0000000000..e43cddcad6 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/SkeletonText/examples/basic-skeletontext.example.ts @@ -0,0 +1,7 @@ +import {extension, SkeletonText} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const skeletonText = root.createComponent(SkeletonText); + + root.appendChild(skeletonText); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/SkeletonTextBlock/SkeletonTextBlock.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/SkeletonTextBlock/SkeletonTextBlock.doc.ts new file mode 100644 index 0000000000..506885a27f --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/SkeletonTextBlock/SkeletonTextBlock.doc.ts @@ -0,0 +1,41 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'SkeletonTextBlock', + description: + 'SkeletonTextBlock is used to provide a low fidelity representation of a block of text before it appears on the page. \n\nOptionally you can use any text content inside `SkeletonTextBlock` to be used as a base for the rendered skeleton', + requires: '', + isVisualComponent: true, + thumbnail: 'skeletontextblock-thumbnail.png', + type: '', + definitions: [ + { + title: 'SkeletonTextBlockProps', + description: '', + type: 'SkeletonTextBlockProps', + }, + ], + category: 'UI components', + subCategory: 'Typography and content', + defaultExample: { + image: 'skeletontextblock-default.png', + codeblock: { + title: 'Basic SkeletonTextBlock', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/SkeletonTextBlock/examples/basic-skeletontextblock.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-skeletontextblock.example.ts', + language: 'js', + }, + ], + }, + }, + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/SkeletonTextBlock/examples/basic-skeletontextblock.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/SkeletonTextBlock/examples/basic-skeletontextblock.example.ts new file mode 100644 index 0000000000..ec1563a888 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/SkeletonTextBlock/examples/basic-skeletontextblock.example.ts @@ -0,0 +1,7 @@ +import {extension, SkeletonTextBlock} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const skeletonTextBlock = root.createComponent(SkeletonTextBlock); + + root.appendChild(skeletonTextBlock); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Spinner/Spinner.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Spinner/Spinner.doc.ts new file mode 100644 index 0000000000..3502132375 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Spinner/Spinner.doc.ts @@ -0,0 +1,57 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Spinner', + description: + 'Spinner is used to notify buyers that their action is being processed. The Spinner is usually used when sending or receiving data from a server.', + requires: '', + thumbnail: 'spinner-thumbnail.png', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'SpinnerProps', + description: '', + type: 'SpinnerProps', + }, + ], + category: 'UI components', + subCategory: 'Feedback and status indicators', + defaultExample: { + image: 'spinner-default.png', + codeblock: { + title: 'Basic Spinner', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Spinner/examples/basic-spinner.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-spinner.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'appearance', + title: 'Appearance', + sectionContent: + '| Value | Description |\n| --- | --- |\n| "accent" | Conveys emphasis and draws attention to the element. |\n| "monochrome" | Takes the color of its parent.|', + }, + ], + related: [ + { + name: 'Progress', + subtitle: 'Component', + url: 'progress', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Spinner/examples/basic-spinner.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Spinner/examples/basic-spinner.example.ts new file mode 100644 index 0000000000..6c28a6168f --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Spinner/examples/basic-spinner.example.ts @@ -0,0 +1,7 @@ +import {extension, Spinner} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const spinner = root.createComponent(Spinner); + + root.appendChild(spinner); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Stepper/Stepper.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Stepper/Stepper.doc.ts new file mode 100644 index 0000000000..1bdfa74dcd --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Stepper/Stepper.doc.ts @@ -0,0 +1,50 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Stepper', + description: + 'Use a stepper to increase or decrease a value, like changing the quantity from 1 to 2.', + requires: '', + thumbnail: 'stepper-thumbnail.png', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'StepperProps', + description: '', + type: 'StepperProps', + }, + ], + category: 'UI components', + subCategory: 'Forms', + defaultExample: { + image: 'stepper-default.png', + codeblock: { + title: 'Basic Stepper', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Stepper/examples/basic-stepper.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-stepper.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + '- Use this component when customers are likely to choose a number within a small range, such as when changing a quantity from one to three.\n\n- If there’s no default number, then consider choosing another component such as a TextField or Select.', + }, + ], + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Stepper/examples/basic-stepper.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Stepper/examples/basic-stepper.example.ts new file mode 100644 index 0000000000..e38824e127 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Stepper/examples/basic-stepper.example.ts @@ -0,0 +1,10 @@ +import {extension, Stepper} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const stepper = root.createComponent(Stepper, { + label: 'Quantity', + value: 1, + }); + + root.appendChild(stepper); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Switch/Switch.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Switch/Switch.doc.ts new file mode 100644 index 0000000000..ea89f2c1d0 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Switch/Switch.doc.ts @@ -0,0 +1,80 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +import {getExample} from '../../helper.docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Switch', + description: + 'Use a switch to represent an on or off state that takes effect immediately when tapped.', + requires: '', + isVisualComponent: true, + thumbnail: 'switch-thumbnail.png', + type: '', + definitions: [ + { + title: 'SwitchProps', + description: '', + type: 'SwitchProps', + }, + ], + category: 'UI components', + subCategory: 'Forms', + defaultExample: { + image: 'switch-default.png', + codeblock: { + title: 'Basic Switch', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Switch/examples/basic-switch.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-switch.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: ` +- The outcome of a switch should take effect immediately when tapped. +- Use for independent settings, like turning on a stand-alone feature. +- Most of the time no call-to-action should be needed as the switch should take effect immediately, but if the experience needs one, use “done” instead of “submit” or “apply”. + +### Content +The label should be a noun. Try explaining the setting out loud to test the name. The name should make sense when you insert it into these statements: + +- You can turn [setting_label] on or off in settings. +- [setting_label] is on. +- [setting_label] is off. + +### Switch vs checkbox +- If the experience requires multiple connected inputs, like a survey, use a checkbox instead of a switch. +- If the experience requires an error state, like agreeing to terms and conditions, use a checkbox instead of a switch. Both on and off options for a switch should always be valid. +- If you’re unsure, default to a checkbox as it’s the more familiar web pattern. + + + `, + }, + ], + examples: { + description: '', + examples: [getExample('ui-components/switch-custom-label', ['jsx', 'js'])], + }, + related: [ + { + name: 'Checkbox', + subtitle: 'Component', + url: 'checkbox', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Switch/examples/basic-switch.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Switch/examples/basic-switch.example.ts new file mode 100644 index 0000000000..79f1e23b09 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Switch/examples/basic-switch.example.ts @@ -0,0 +1,9 @@ +import {extension, Switch} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const baseSwitch = root.createComponent(Switch, { + accessibilityLabel: 'my-switch', + }); + + root.appendChild(baseSwitch); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Tag/Tag.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Tag/Tag.doc.ts new file mode 100644 index 0000000000..88e7b51547 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Tag/Tag.doc.ts @@ -0,0 +1,41 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Tag', + description: + 'A Tag is used to help label, organize or categorize objects. It is commonly used in Checkout to display the discounts applied to a cart.', + requires: '', + thumbnail: 'tag-thumbnail.png', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'TagProps', + description: '', + type: 'TagProps', + }, + ], + category: 'UI components', + subCategory: 'Typography and content', + defaultExample: { + image: 'tag-default.png', + codeblock: { + title: 'Basic Tag', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Tag/examples/basic-tag.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-tag.example.ts', + language: 'js', + }, + ], + }, + }, + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Tag/examples/basic-tag.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Tag/examples/basic-tag.example.ts new file mode 100644 index 0000000000..4f2dbd21dc --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Tag/examples/basic-tag.example.ts @@ -0,0 +1,7 @@ +import {extension, Tag} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const tag = root.createComponent(Tag, {icon: 'discount'}, 'SPRING'); + + root.appendChild(tag); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Text/Text.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Text/Text.doc.ts new file mode 100644 index 0000000000..1b1b41f099 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Text/Text.doc.ts @@ -0,0 +1,76 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Text', + description: + 'Text is used to visually style and provide semantic value for a small piece of text content.', + requires: '', + thumbnail: 'text-thumbnail.png', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'TextProps', + description: '', + type: 'TextProps', + }, + ], + category: 'UI components', + subCategory: 'Typography and content', + defaultExample: { + image: 'text-default.png', + codeblock: { + title: 'Basic Text', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Text/examples/basic-text.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-text.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'appearance', + title: 'Appearance', + sectionContent: + '| Value | Description |\n| --- | --- |\n| "accent" | Conveys emphasis and draws attention to the element. |\n| "subdued" | Conveys a subdued or disabled state for the element. |\n| "info" | Conveys that the element is informative or has information. |\n| "success" | Convey a successful interaction. |\n| "warning" | Convey something needs attention or an action needs to be taken. |\n| "critical" | Conveys a problem has arisen. |', + }, + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + '- Use larger text to emphasize content that’s not a heading, such as a price total.\n\n- Create contrast between more and less important text with properties such as `size` and `subdued`.', + }, + ], + related: [ + { + name: 'Heading', + subtitle: 'Component', + url: 'heading', + type: 'Component', + }, + { + name: 'HeadingGroup', + subtitle: 'Component', + url: 'headinggroup', + type: 'Component', + }, + { + name: 'TextBlock', + subtitle: 'Component', + url: 'textblock', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Text/examples/basic-text.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Text/examples/basic-text.example.ts new file mode 100644 index 0000000000..63a0a65717 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Text/examples/basic-text.example.ts @@ -0,0 +1,14 @@ +import {extension, Text, BlockStack} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const text = root.createComponent(BlockStack, undefined, [ + root.createComponent(Text, {size: 'extraSmall'}, 'Total'), + root.createComponent(Text, {size: 'small'}, 'Total'), + root.createComponent(Text, {size: 'base'}, 'Total'), + root.createComponent(Text, {size: 'medium'}, 'Total'), + root.createComponent(Text, {size: 'large'}, 'Total'), + root.createComponent(Text, {size: 'extraLarge'}, 'Total'), + ]); + + root.appendChild(text); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/TextBlock/TextBlock.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/TextBlock/TextBlock.doc.ts new file mode 100644 index 0000000000..ec33c46a27 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/TextBlock/TextBlock.doc.ts @@ -0,0 +1,76 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'TextBlock', + description: + 'Text block is used to render a block of text that occupies the full width available, like a paragraph.', + requires: '', + thumbnail: 'textblock-thumbnail.png', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'TextBlockProps', + description: '', + type: 'TextBlockProps', + }, + ], + category: 'UI components', + subCategory: 'Typography and content', + defaultExample: { + image: 'textblock-default.png', + codeblock: { + title: 'Basic TextBlock', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/TextBlock/examples/basic-textblock.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-textblock.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'appearance', + title: 'Appearance', + sectionContent: + '| Value | Description |\n| --- | --- |\n| "accent" | Conveys emphasis and draws attention to the element. |\n| "subdued" | Conveys a subdued or disabled state for the element. |\n| "info" | Conveys that the element is informative or has information. |\n| "success" | Convey a successful interaction. |\n| "warning" | Convey something needs attention or an action needs to be taken. |\n| "critical" | Conveys a problem has arisen. |', + }, + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + '- Create contrast between more and less important text with properties such as `size`, `emphasis`, and `appearance`.', + }, + ], + related: [ + { + name: 'Heading', + subtitle: 'Component', + url: 'heading', + type: 'Component', + }, + { + name: 'HeadingGroup', + subtitle: 'Component', + url: 'headinggroup', + type: 'Component', + }, + { + name: 'Text', + subtitle: 'Component', + url: 'text', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/TextBlock/examples/basic-textblock.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/TextBlock/examples/basic-textblock.example.ts new file mode 100644 index 0000000000..ce9438bd24 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/TextBlock/examples/basic-textblock.example.ts @@ -0,0 +1,22 @@ +import { + extension, + TextBlock, + BlockStack, +} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const textBlock = root.createComponent(BlockStack, undefined, [ + root.createComponent( + TextBlock, + undefined, + 'We have a 30-day return policy, which means you have 30 days after receiving your item to request a return.', + ), + root.createComponent( + TextBlock, + undefined, + 'To be eligible for a return, your item must be in the same condition that you received it, unworn or unused, with tags, and in its original packaging. You’ll also need the receipt or proof of purchase.', + ), + ]); + + root.appendChild(textBlock); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/TextField/TextField.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/TextField/TextField.doc.ts new file mode 100644 index 0000000000..02cd6ce70c --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/TextField/TextField.doc.ts @@ -0,0 +1,49 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'TextField', + description: 'Use a text field to get text input from a customer.', + requires: '', + thumbnail: 'textfield-thumbnail.png', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'TextFieldProps', + description: '', + type: 'TextFieldProps', + }, + ], + category: 'UI components', + subCategory: 'Forms', + defaultExample: { + image: 'textfield-default.png', + codeblock: { + title: 'Basic TextField', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/TextField/examples/basic-textfield.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-textfield.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + '- Clearly label text fields so that it’s obvious what customers should enter.\n\n- Label text fields as Optional when input isn’t required. For example, use the label First name (optional).\n\n- Don’t have optional fields pass true to the required property.', + }, + ], + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/TextField/examples/basic-textfield.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/TextField/examples/basic-textfield.example.ts new file mode 100644 index 0000000000..2b09e28ed1 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/TextField/examples/basic-textfield.example.ts @@ -0,0 +1,9 @@ +import {extension, TextField} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const textfield = root.createComponent(TextField, { + label: 'Last name', + }); + + root.appendChild(textfield); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ToggleButton/ToggleButton.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ToggleButton/ToggleButton.doc.ts new file mode 100644 index 0000000000..f021ec6cb3 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ToggleButton/ToggleButton.doc.ts @@ -0,0 +1,48 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'ToggleButton', + description: + 'Options inside a [ToggleButtonGroup](/docs/api/checkout-ui-extensions/components/forms/togglebuttongroup).', + thumbnail: 'togglebutton-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'ToggleButtonProps', + description: '', + type: 'ToggleButtonProps', + }, + ], + category: 'UI components', + subCategory: 'Forms', + defaultExample: { + image: 'togglebutton-default.png', + codeblock: { + title: 'Basic ToggleButton', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/ToggleButton/examples/basic-togglebutton.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-togglebutton.example.ts', + language: 'js', + }, + ], + }, + }, + related: [ + { + name: 'ToggleButtonGroup', + subtitle: 'Component', + url: 'togglebuttongroup', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ToggleButton/examples/basic-togglebutton.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ToggleButton/examples/basic-togglebutton.example.ts new file mode 100644 index 0000000000..7e9a950734 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ToggleButton/examples/basic-togglebutton.example.ts @@ -0,0 +1,20 @@ +import { + extension, + ToggleButtonGroup, + ToggleButton, +} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const toggleButtonGroup = root.createComponent( + ToggleButtonGroup, + { + value: 'none', + onChange: (value) => { + console.log(`onChange event with value: ${value}`); + }, + }, + [root.createComponent(ToggleButton, {id: 'none'}, 'None')], + ); + + root.appendChild(toggleButtonGroup); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ToggleButtonGroup/ToggleButtonGroup.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ToggleButtonGroup/ToggleButtonGroup.doc.ts new file mode 100644 index 0000000000..693e28a72c --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ToggleButtonGroup/ToggleButtonGroup.doc.ts @@ -0,0 +1,80 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +import {getExample} from '../../helper.docs'; + +const togglebuttongroupTimePicking = getExample( + 'ui-components/togglebuttongroup-time-picking', + ['jsx', 'js'], +); + +const data: ReferenceEntityTemplateSchema = { + name: 'ToggleButtonGroup', + description: + '`ToggleButtonGroup` allows you to make a single choice out of the number of options provided. This is similar to the [ChoiceList](/docs/api/checkout-ui-extensions/components/forms/choicelist) component, but without controls such as checkbox or radio button.\n\nYou can utilize our layout components to arrange `ToggleButtonGroup`.', + thumbnail: 'togglebuttongroup-thumbnail.png', + requires: '', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'ToggleButtonGroupProps', + description: '', + type: 'ToggleButtonGroupProps', + }, + ], + category: 'UI components', + subCategory: 'Forms', + defaultExample: { + image: 'togglebuttongroup-default.png', + codeblock: { + title: 'Basic ToggleButtonGroup', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/ToggleButtonGroup/examples/basic-togglebuttongroup.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-togglebuttongroup.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + 'ToggleButtonGroup is a component designed for streamlined single-choice selection, without any additional details associated with the selection. If you need to allow multiple selections or present associated details, it is recommended to use [ChoiceList](/docs/api/checkout-ui-extensions/components/forms/choicelist) instead.', + sectionSubContent: [ + { + title: 'Label and order', + sectionContent: + 'Use descriptive and concise labels for each Toggle button, and maintain consistency in the terminology used across options. Arrange the Toggle buttons in a logical order that makes sense to users. Consider factors such as alphabetical order, chronological order, or order of importance, depending on the context.', + }, + { + title: 'Number of Toggle buttons', + sectionContent: + 'Avoid overwhelming users with too many Toggle buttons. Ideally, limit the number of choices to a manageable amount, typically between 2 and 7, to prevent decision fatigue and maintain clarity.', + }, + ], + }, + ], + examples: { + description: '', + examples: [togglebuttongroupTimePicking], + }, + related: [ + { + name: 'ToggleButton', + subtitle: 'Component', + url: 'togglebutton', + type: 'Component', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/ToggleButtonGroup/examples/basic-togglebuttongroup.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/ToggleButtonGroup/examples/basic-togglebuttongroup.example.ts new file mode 100644 index 0000000000..94470c5625 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/ToggleButtonGroup/examples/basic-togglebuttongroup.example.ts @@ -0,0 +1,76 @@ +import { + extension, + ToggleButtonGroup, + ToggleButton, + InlineLayout, + View, + BlockStack, + Text, +} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const toggleButtonGroup = root.createComponent( + ToggleButtonGroup, + { + value: 'none', + onChange: (value) => { + console.log(`onChange event with value: ${value}`); + }, + }, + [ + root.createComponent(InlineLayout, {spacing: 'base'}, [ + root.createComponent( + ToggleButton, + {id: 'none'}, + root.createComponent( + View, + { + blockAlignment: 'center', + inlineAlignment: 'center', + minBlockSize: 'fill', + }, + 'None', + ), + ), + root.createComponent( + ToggleButton, + {id: 'points-100'}, + root.createComponent( + BlockStack, + {inlineAlignment: 'center', spacing: 'none'}, + [ + root.createComponent(Text, undefined, '100'), + root.createComponent(Text, {appearance: 'subdued'}, 'points'), + ], + ), + ), + root.createComponent( + ToggleButton, + {id: 'points-200'}, + root.createComponent( + BlockStack, + {inlineAlignment: 'center', spacing: 'none'}, + [ + root.createComponent(Text, undefined, '200'), + root.createComponent(Text, {appearance: 'subdued'}, 'points'), + ], + ), + ), + root.createComponent( + ToggleButton, + {id: 'points-300'}, + root.createComponent( + BlockStack, + {inlineAlignment: 'center', spacing: 'none'}, + [ + root.createComponent(Text, undefined, '300'), + root.createComponent(Text, {appearance: 'subdued'}, 'points'), + ], + ), + ), + ]), + ], + ); + + root.appendChild(toggleButtonGroup); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Tooltip/Tooltip.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Tooltip/Tooltip.doc.ts new file mode 100644 index 0000000000..666c006cfb --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Tooltip/Tooltip.doc.ts @@ -0,0 +1,50 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'Tooltip', + description: + 'Tooltips are floating labels that briefly explain the function of a user interface element. They must be specified inside the `overlay` prop of an activator component. Currently, activator components are `Button`, `Link`, and `Pressable`.\n\nThe library automatically applies the [WAI-ARIA Tooltip Widget pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/) to both the activator and the tooltip content. Expect screen readers to read the tooltip content when the user focuses the activator.', + requires: '', + thumbnail: 'tooltip-thumbnail.png', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'TooltipProps', + description: '', + type: 'TooltipProps', + }, + ], + category: 'UI components', + subCategory: 'Overlays', + defaultExample: { + image: 'tooltip-default.png', + codeblock: { + title: 'Basic Tooltip', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/Tooltip/examples/basic-tooltip.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-tooltip.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'best-practices', + title: 'Best Practices', + sectionContent: + 'Use tooltips if:\n\n- It’s used for showing information only.\n\n- The information contained in it is not needed by someone to complete their checkout.\n\n- The information can be written in a sentence.', + }, + ], + related: [], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/Tooltip/examples/basic-tooltip.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/Tooltip/examples/basic-tooltip.example.ts new file mode 100644 index 0000000000..2d0591ad84 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/Tooltip/examples/basic-tooltip.example.ts @@ -0,0 +1,23 @@ +import { + extension, + Icon, + Pressable, + Tooltip, +} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const tooltipFragment = root.createFragment(); + const tooltip = root.createComponent( + Tooltip, + {}, + 'In case we need to contact you about your order', + ); + tooltipFragment.appendChild(tooltip); + const pressable = root.createComponent( + Pressable, + {overlay: tooltipFragment}, + [root.createComponent(Icon, {source: 'questionFill'})], + ); + + root.appendChild(pressable); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/View/View.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/components/View/View.doc.ts new file mode 100644 index 0000000000..a89b014045 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/View/View.doc.ts @@ -0,0 +1,57 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'View', + description: + 'View is a generic container component. Its contents will always be their “natural” size, so this component can be useful in layout components (like `Grid`, `BlockStack`, `InlineStack`) that would otherwise stretch their children to fit.', + requires: '', + thumbnail: 'view-thumbnail.png', + isVisualComponent: true, + type: '', + definitions: [ + { + title: 'ViewProps', + description: '', + type: 'ViewProps', + }, + ], + category: 'UI components', + subCategory: 'Layout and structure', + defaultExample: { + image: 'view-default.png', + codeblock: { + title: 'Basic View', + tabs: [ + { + title: 'React', + code: '../../../../../../ui-extensions-react/src/surfaces/customer-account/components/View/examples/basic-view.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/basic-view.example.ts', + language: 'js', + }, + ], + }, + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'accessibility-roles', + title: 'Accessibility roles', + sectionContent: + '| Value | Description |\n| --- | --- |\n| "main" | Used to indicate the primary content. |\n| "header" | Used to indicate the component is a header. |\n| "footer" | Used to display information such as copyright information, navigation links, and privacy statements. |\n| "section" | Used to indicate a generic section. |\n| "complementary" | Used to designate a supporting section that relates to the main content. |\n| "navigation" | Used to identify major groups of links used for navigating. |\n| "orderedList" | Used to identify a list of ordered items. |\n| "listItem" | Used to identify an item inside a list of items. |\n| "unorderedList" | Used to identify a list of unordered items. |\n| "separator" | Used to indicates the component acts as a divider that separates and distinguishes sections of content. |\n| "status" | Used to define a live region containing advisory information for the user that is not important enough to be an alert. |\n| "alert" | Used for important, and usually time-sensitive, information. |', + }, + ], + related: [ + { + subtitle: 'Utility', + name: 'StyleHelper', + url: '/docs/api/checkout-ui-extensions/unstable/components/utilities/stylehelper', + type: 'utility', + }, + ], +}; + +export default data; diff --git a/packages/ui-extensions/src/surfaces/customer-account/components/View/examples/basic-view.example.ts b/packages/ui-extensions/src/surfaces/customer-account/components/View/examples/basic-view.example.ts new file mode 100644 index 0000000000..9ed27629ff --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/components/View/examples/basic-view.example.ts @@ -0,0 +1,11 @@ +import {extension, View} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const view = root.createComponent( + View, + {border: 'base', padding: 'base'}, + 'View', + ); + + root.appendChild(view); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/helper.docs.ts b/packages/ui-extensions/src/surfaces/customer-account/helper.docs.ts new file mode 100644 index 0000000000..c17a7e7594 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/helper.docs.ts @@ -0,0 +1,232 @@ +import type {CodeTabType, ExampleType} from '@shopify/generate-docs'; + +const examplePath = '../../../../../docs/surfaces/checkout/reference/examples'; + +type NonEmptyArray = [T, ...T[]]; +type ExtensionExampleLanguage = 'js' | 'jsx'; +type ExtensionCodeTabConfig = Record< + ExtensionExampleLanguage, + { + title: string; + fileExtension: 'ts' | 'tsx'; + } +>; +const codeExampleTabConfig: ExtensionCodeTabConfig = { + js: { + title: 'JavaScript', + fileExtension: 'ts', + }, + jsx: { + title: 'React', + fileExtension: 'tsx', + }, +}; + +/** + * Returns all examples available, specified with a key for reference. + */ +export function getExamples( + languages: NonEmptyArray, +): Record { + if (!languages || languages.length === 0) { + throw new HelperDocsError( + 'You must define at least one extension code language context you wish to retrieve the example(s) for.', + ); + } + /** + * Provides the code tab for the requested languages in "JavaScript" and "React". + */ + function getExtensionCodeTabs(name: string): CodeTabType[] { + return languages.map((language) => { + return { + code: `${examplePath}/${name}.example.${codeExampleTabConfig[language].fileExtension}`, + language, + title: codeExampleTabConfig[language].title, + }; + }); + } + + // Add new examples here that can be shared across multiples pages. + return { + 'ui-components/checkbox-links': { + description: + 'To provide buyers with additional information or references, couple it with link components seamlessly within checkbox components. This can be done by including links as part of the checkbox label in the checkbox. This will provide an easy way to access relevant content that buyers may need.', + image: 'checkbox-links.png', + codeblock: { + title: 'Embedding links in checkbox components', + tabs: getExtensionCodeTabs('ui-components/checkbox-links'), + }, + }, + 'ui-components/clipboarditem-qrcode': { + description: + 'When displaying a QR code, include an alternative way for the user to get the content', + image: 'clipboard-qrcode.png', + codeblock: { + title: "Copying content of a QR code to the user's clipboard", + tabs: getExtensionCodeTabs('ui-components/clipboarditem-qrcode'), + }, + }, + 'ui-components/clipboarditem-oncopy': { + description: + 'Use the onCopy property to display an icon that swaps to a checkmark when the text is copied.', + image: 'clipboard-oncopy.png', + codeblock: { + title: 'Swapping an icon when the text is copied', + tabs: getExtensionCodeTabs('ui-components/clipboarditem-oncopy'), + }, + }, + 'ui-components/disclosure-and-alignment': { + description: + 'Use the Disclosure component to simplify the user experience and reveal interfaces only when the customer requests it. It also demonstrates how a combination of inline and block layout components can improve the readability of information. By employing these strategies, users can easily scan and comprehend the content, making for a better user experience overall.', + image: 'disclosure-and-alignment.gif', + codeblock: { + title: + 'Strategies for simplifying layout and aligning content using Disclosure and Inline/Block Layout components.', + tabs: getExtensionCodeTabs('ui-components/disclosure-and-alignment'), + }, + }, + 'ui-components/loading-skeletons': { + description: + 'When adding content to a layout, incorporate a skeleton loader that renders the approximate size and position of the content during loading. This will provide a seamless transition from skeleton loaders to the content, and prevent any layout shift when the resulting content loads.', + image: 'loading-skeletons.gif', + codeblock: { + title: + 'Using skeleton loaders to prevent layout shifts on content load.', + tabs: getExtensionCodeTabs('ui-components/loading-skeletons'), + }, + }, + 'ui-components/togglebuttongroup-time-picking': { + image: 'togglebuttongroup-time-picking.png', + description: `The ToggleButtonGroup component is ideal for a small set of options. It allows for easy scanning of available choices. Also the component’s big tap target makes it a good choice for enhanced mobile experience. However, in a grid layout, having more than 6 ToggleButtons can get overwhelming and take up too much vertical space. When there are more than 6 choices, consider using the [Select](/docs/api/checkout-ui-extensions/components/forms/select) component instead. `, + codeblock: { + title: 'Displaying a small set of time choices', + tabs: getExtensionCodeTabs( + 'ui-components/togglebuttongroup-time-picking', + ), + }, + }, + 'ui-components/choicelist-survey': { + description: `The base variant’s flexibility allows for the creation of Likert scales using the ChoiceList component. By utilizing layout components, you can easily structure rows and columns for this purpose.`, + image: 'choicelist-survey.png', + codeblock: { + title: 'Custom survey using the base variant', + tabs: getExtensionCodeTabs('ui-components/choicelist-survey'), + }, + }, + 'ui-components/choicelist-details': { + description: `The ChoiceList’s group variant, combined with the details property, allows for the conditional display of information when needed.`, + image: 'choicelist-details.png', + codeblock: { + title: 'Collecting additional information', + tabs: getExtensionCodeTabs('ui-components/choicelist-details'), + }, + }, + 'ui-components/choicelist-time-picking': { + description: `The ChoiceList component is great for presenting a concise list of options, particularly when showcasing time ranges due to its ample horizontal space. However, if there’s more than 5 choices, use the [Select](/docs/api/checkout-ui-extensions/components/forms/select) component instead.`, + image: 'choicelist-time-picking.png', + codeblock: { + title: 'Displaying a short list of time choices', + tabs: getExtensionCodeTabs('ui-components/choicelist-time-picking'), + }, + }, + 'ui-components/sheet-consent': { + description: + 'The Sheet component can be used to display privacy consent preferences in the Checkout interface. Sheet can be defaulted to open for this use case.\n\n This component requires access to [Customer Privacy API](/docs/api/checkout-ui-extensions/unstable/apis/customer-privacy) to be rendered.', + codeblock: { + title: 'Using Sheet to display consent preferences', + tabs: getExtensionCodeTabs('ui-components/sheet-consent'), + }, + }, + 'ui-components/sheet-description-preferences': { + description: + 'In order to save space in the action slot, secondary actions can be placed in the content area.', + image: 'sheet-description-preferences.png', + codeblock: { + title: 'Preferences button is in the description as a link', + tabs: getExtensionCodeTabs( + 'ui-components/sheet-description-preferences', + ), + }, + }, + 'ui-components/sheet-layout-content': { + description: + 'The description can take in layout components to allow for different types of content to be structured in specific ways.', + image: 'sheet-layout-content.png', + codeblock: { + title: 'Using layout component in the description ', + tabs: getExtensionCodeTabs('ui-components/sheet-layout-content'), + }, + }, + 'ui-components/sheet-icon-button-preferences': { + description: + 'An icon button can be used in the secondary actions area to allow for more space for the primary actions.', + image: 'sheet-icon-button-preferences.png', + codeblock: { + title: 'Icon button used for preferences', + tabs: getExtensionCodeTabs( + 'ui-components/sheet-icon-button-preferences', + ), + }, + }, + 'ui-components/switch-custom-label': { + description: + 'This example demonstrates pairing the switch with a custom label and layout while keeping it accessible to screen readers.', + image: 'switch-custom-label.png', + codeblock: { + title: 'Custom label', + tabs: getExtensionCodeTabs('ui-components/switch-custom-label'), + }, + }, + 'ui-components/progress-determinate-state': { + description: + 'In a determinate state, [TextBlock](../titles-and-text/textblock) or [Text](../titles-and-text/text) components can be used to communicate what the progress bar is tracking, and to set clear expectations about the current progress.', + image: 'progress-determinate.png', + codeblock: { + title: 'Determinate state', + tabs: getExtensionCodeTabs('ui-components/progress-determinate-state'), + }, + }, + 'ui-components/qrcode-image': { + description: + 'The QRCode component can display an image in the center. Adding a logo can increase brand trust and set expectations for the action when scanning.', + image: 'qrcode-image.png', + codeblock: { + title: 'With logo', + tabs: getExtensionCodeTabs('ui-components/qrcode-image'), + }, + }, + 'ui-components/qrcode-fill-size': { + description: + 'In most cases the default size should work well. If you need a different size, use `fill` to make it grow to the size of its parent container.', + image: 'qrcode-fill-size.png', + codeblock: { + title: 'Fill size', + tabs: getExtensionCodeTabs('ui-components/qrcode-fill-size'), + }, + }, + }; +} + +/** + * Returns a specific `Example` by name, as specified in `getExamples()`. + * Specify whether you want to include both `js` and `jsx`examples or just one. + */ +export function getExample( + name: string, + languages: NonEmptyArray = ['js'], +): ExampleType { + const example = getExamples(languages)[name]; + if (!example) { + throw new HelperDocsError( + `Could not find a matching example with the name "${name}". Does it exist within the file "docs/reference/helper.docs.ts" in getExamples()?`, + ); + } + return example; +} + +class HelperDocsError extends Error { + name = 'HelperDocsError'; +} + +export const REQUIRES_PROTECTED_CUSTOMER_DATA = + 'access to [protected customer data](/docs/apps/store/data-protection/protected-customer-data) for some properties.'; diff --git a/packages/ui-extensions/src/surfaces/customer-account/style/examples/defaultstyle.example.tsx b/packages/ui-extensions/src/surfaces/customer-account/style/examples/defaultstyle.example.tsx new file mode 100644 index 0000000000..3f462557cb --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/style/examples/defaultstyle.example.tsx @@ -0,0 +1,9 @@ + + Content + Content + diff --git a/packages/ui-extensions/src/surfaces/customer-account/style/examples/hiding.example.tsx b/packages/ui-extensions/src/surfaces/customer-account/style/examples/hiding.example.tsx new file mode 100644 index 0000000000..78e0af35c7 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/style/examples/hiding.example.tsx @@ -0,0 +1,8 @@ + + Content + diff --git a/packages/ui-extensions/src/surfaces/customer-account/style/examples/simplecondition.example.tsx b/packages/ui-extensions/src/surfaces/customer-account/style/examples/simplecondition.example.tsx new file mode 100644 index 0000000000..f20d3439ef --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/style/examples/simplecondition.example.tsx @@ -0,0 +1,9 @@ + alert('press')} + border={Style.default(['base', 'dotted']).when( + {viewportInlineSize: {min: 'small'}}, + ['base', 'dotted', 'none', 'base'], + )} +> + Content + diff --git a/packages/ui-extensions/src/surfaces/customer-account/style/examples/style.example.ts b/packages/ui-extensions/src/surfaces/customer-account/style/examples/style.example.ts new file mode 100644 index 0000000000..71b3639511 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/style/examples/style.example.ts @@ -0,0 +1,18 @@ +import {Style, View, extension} from '@shopify/ui-extensions/customer-account'; + +export default extension('customer-account.page.render', (root) => { + const view = root.createComponent( + View, + { + border: 'base', + padding: 'base', + maxInlineSize: Style.default(200) + .when({viewportInlineSize: {min: 'small'}}, 300) + .when({viewportInlineSize: {min: 'medium'}}, 400) + .when({viewportInlineSize: {min: 'large'}}, 800), + }, + 'Responsive Content', + ); + + root.appendChild(view); +}); diff --git a/packages/ui-extensions/src/surfaces/customer-account/style/examples/style.example.tsx b/packages/ui-extensions/src/surfaces/customer-account/style/examples/style.example.tsx new file mode 100644 index 0000000000..949ef11586 --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/style/examples/style.example.tsx @@ -0,0 +1,22 @@ +import { + reactExtension, + Style, + View, +} from '@shopify/ui-extensions-react/customer-account'; + +export default reactExtension('customer-account.page.render', () => ( + +)); + +function Extension() { + return ( + + Responsive Content + + ); +} diff --git a/packages/ui-extensions/src/surfaces/customer-account/style/style.doc.ts b/packages/ui-extensions/src/surfaces/customer-account/style/style.doc.ts new file mode 100644 index 0000000000..71fdd1b91e --- /dev/null +++ b/packages/ui-extensions/src/surfaces/customer-account/style/style.doc.ts @@ -0,0 +1,180 @@ +import type {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'; + +const data: ReferenceEntityTemplateSchema = { + name: 'StyleHelper', + description: + 'This is a helper for authoring conditional values for property styles.\n\nWrite complex conditional styles based on one or more conditions, such as viewport sizes and interactive states, in a concise and expressive way.', + requires: '', + isVisualComponent: false, + type: '', + definitions: [ + { + title: 'StyleHelper', + description: '', + type: 'DocsStyle', + }, + ], + category: 'UI components', + subCategory: 'Layout and structure', + defaultExample: { + codeblock: { + tabs: [ + { + title: 'React', + code: './examples/style.example.tsx', + language: 'tsx', + }, + { + title: 'JS', + code: './examples/style.example.ts', + language: 'js', + }, + ], + title: 'Import the Style helper', + }, + }, + examples: { + description: 'This section provides examples of conditions.', + examples: [ + { + description: + "Default styling can be combined with specific conditions. In this example, the Grid's children will be stacked by default and side by side on viewports above the small breakpoint.", + codeblock: { + title: 'Default Style With Conditions', + tabs: [ + { + title: 'React', + code: './examples/defaultstyle.example.tsx', + language: 'tsx', + }, + ], + }, + }, + { + description: + "Using simple conditional styling enables you to specify a styling change when a condition is met. In this example, the View's padding will be loose on hover.", + codeblock: { + title: 'Simple Condition', + tabs: [ + { + title: 'React', + code: './examples/simplecondition.example.tsx', + language: 'tsx', + }, + ], + }, + }, + { + description: + 'Using the `display` property with conditional styles enables you to hide content for certain viewport sizes. In this example, the View will be hidden on small and above screen sizes.', + codeblock: { + title: 'Conditionally hiding content', + tabs: [ + { + title: 'React', + code: './examples/hiding.example.tsx', + language: 'tsx', + }, + ], + }, + }, + ], + }, + subSections: [ + { + type: 'Generic', + anchorLink: 'conditions', + title: 'Conditions', + sectionContent: + 'The following conditions are supported for conditional styles.\n\nMultiple conditions can be set on the same `when` method.\n\n \n\n| Name | Type | Description |\n| --- | --- | --- |\n| "hover" | true | This condition is met when an element is hovered on with the cursor (mouse pointer). |\n| "focus" | true | This condition is met when an element is clicked, tapped on or selected using the Tab key.|\n| viewportInlineSize | {min: "small" | "medium" | "large"} | This condition is met when the device matches the minimum width.|', + }, + ], + related: [ + { + name: 'BlockLayout', + subtitle: 'Component', + url: 'blocklayout', + type: 'Component', + }, + { + name: 'BlockSpacer', + subtitle: 'Component', + url: 'blockspacer', + type: 'Component', + }, + { + name: 'BlockStack', + subtitle: 'Component', + url: 'blockstack', + type: 'Component', + }, + { + name: 'Grid', + subtitle: 'Component', + url: 'grid', + type: 'Component', + }, + { + name: 'GridItem', + subtitle: 'Component', + url: 'griditem', + type: 'Component', + }, + { + name: 'Image', + subtitle: 'Component', + url: 'image', + type: 'Component', + }, + { + name: 'InlineLayout', + subtitle: 'Component', + url: 'inlinelayout', + type: 'Component', + }, + { + name: 'InlineSpacer', + subtitle: 'Component', + url: 'inlinespacer', + type: 'Component', + }, + { + name: 'InlineStack', + subtitle: 'Component', + url: 'inlinestack', + type: 'Component', + }, + { + name: 'List', + subtitle: 'Component', + url: 'list', + type: 'Component', + }, + { + name: 'Pressable', + subtitle: 'Component', + url: 'pressable', + type: 'Component', + }, + { + name: 'ScrollView', + subtitle: 'Component', + url: 'scrollview', + type: 'Component', + }, + { + name: 'SkeletonImage', + subtitle: 'Component', + url: 'skeletonimage', + type: 'Component', + }, + { + name: 'View', + subtitle: 'Component', + url: 'view', + type: 'Component', + }, + ], +}; + +export default data;