diff --git a/.changeset/angry-jeans-kneel.md b/.changeset/angry-jeans-kneel.md new file mode 100644 index 0000000000..4b9053c91f --- /dev/null +++ b/.changeset/angry-jeans-kneel.md @@ -0,0 +1,6 @@ +--- +'@twilio-paste/detail-text': major +'@twilio-paste/core': minor +--- + +[Detail Text] create the new DetailText component, meant to be used for short pieces of secondary text content. diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index 1b7b646fd6..9711f00442 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -32,6 +32,7 @@ "/packages/paste-core/components/date-picker", "/packages/paste-core/components/description-list", "/packages/paste-design-tokens", + "/packages/paste-core/components/detail-text", "/packages/paste-core/components/disclosure", "/packages/paste-core/primitives/disclosure", "/packages/paste-core/components/display-heading", diff --git a/cypress/integration/sitemap-vrt/constants.ts b/cypress/integration/sitemap-vrt/constants.ts index b1f98c3c77..bf404e8bd4 100644 --- a/cypress/integration/sitemap-vrt/constants.ts +++ b/cypress/integration/sitemap-vrt/constants.ts @@ -26,6 +26,7 @@ export const SITEMAP = [ '/components/chat-log/', '/components/checkbox/', '/components/data-grid/', + '/components/detail-text', '/components/description-list/', '/components/display-heading/', '/components/display-pill-group/', diff --git a/packages/paste-codemods/tools/.cache/mappings.json b/packages/paste-codemods/tools/.cache/mappings.json index 2631fd7592..fc69cc19d0 100644 --- a/packages/paste-codemods/tools/.cache/mappings.json +++ b/packages/paste-codemods/tools/.cache/mappings.json @@ -71,6 +71,7 @@ "DescriptionListSet": "@twilio-paste/core/description-list", "DescriptionListTerm": "@twilio-paste/core/description-list", "StyledDescriptionListSet": "@twilio-paste/core/description-list", + "DetailText": "@twilio-paste/core/detail-text", "AnimatedDisclosureContent": "@twilio-paste/core/disclosure", "Disclosure": "@twilio-paste/core/disclosure", "DisclosureContent": "@twilio-paste/core/disclosure", diff --git a/packages/paste-core/components/detail-text/CHANGELOG.md b/packages/paste-core/components/detail-text/CHANGELOG.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/paste-core/components/detail-text/__tests__/index.spec.tsx b/packages/paste-core/components/detail-text/__tests__/index.spec.tsx new file mode 100644 index 0000000000..774fc40b34 --- /dev/null +++ b/packages/paste-core/components/detail-text/__tests__/index.spec.tsx @@ -0,0 +1,72 @@ +import * as React from 'react'; +import {render, screen} from '@testing-library/react'; +import {CustomizationProvider} from '@twilio-paste/customization'; + +import {DetailText} from '../src'; + +describe('DetailText', () => { + it('should render', () => { + render(test); + expect(screen.getByText('test')).toBeDefined(); + }); + + it('should render the element based on the as prop', () => { + render(test); + expect(screen.getByRole('listitem')).toBeDefined(); + }); + + describe('Customization', () => { + it('should set default data paste element attribute', () => { + render(test); + + expect(screen.getByTestId('detail-text').dataset.pasteElement).toEqual('DETAIL_TEXT'); + }); + + it('should set custom data paste element attribute', () => { + render( + + test + + ); + + expect(screen.getByTestId('detail-text').dataset.pasteElement).toEqual('MY_DETAIL_TEXT'); + }); + + it('should add custom styles', () => { + render( + + test + + ); + expect(screen.getByTestId('detail-text')).toHaveStyleRule('color', 'rgb(109, 46, 209)'); + }); + + it('should add custom styles with a custom data paste element attribute', () => { + render( + + + test + + + ); + + expect(screen.getByTestId('detail-text')).toHaveStyleRule('color', 'rgb(109, 46, 209)'); + }); + }); +}); diff --git a/packages/paste-core/components/detail-text/build.js b/packages/paste-core/components/detail-text/build.js new file mode 100644 index 0000000000..a4edeab49b --- /dev/null +++ b/packages/paste-core/components/detail-text/build.js @@ -0,0 +1,3 @@ +const {build} = require('../../../../tools/build/esbuild'); + +build(require('./package.json')); diff --git a/packages/paste-core/components/detail-text/package.json b/packages/paste-core/components/detail-text/package.json new file mode 100644 index 0000000000..74ec03c7a9 --- /dev/null +++ b/packages/paste-core/components/detail-text/package.json @@ -0,0 +1,54 @@ +{ + "name": "@twilio-paste/detail-text", + "version": "0.0.0", + "category": "typography", + "status": "production", + "description": "Detail text is typography used for short pieces of secondary text content.", + "author": "Twilio Inc.", + "license": "MIT", + "main:dev": "src/index.tsx", + "main": "dist/index.js", + "module": "dist/index.es.js", + "types": "dist/index.d.ts", + "sideEffects": false, + "publishConfig": { + "access": "public" + }, + "files": [ + "dist" + ], + "scripts": { + "build": "yarn clean && NODE_ENV=production node build.js && tsc", + "build:js": "NODE_ENV=development node build.js", + "build:props": "typedoc --tsconfig ./tsconfig.json --json ./dist/prop-types.json", + "clean": "rm -rf ./dist", + "tsc": "tsc" + }, + "peerDependencies": { + "@twilio-paste/animation-library": "^0.3.2", + "@twilio-paste/box": "^7.0.0", + "@twilio-paste/customization": "^5.0.0", + "@twilio-paste/design-tokens": "^8.0.0", + "@twilio-paste/style-props": "^6.0.0", + "@twilio-paste/styling-library": "^1.0.0", + "@twilio-paste/theme": "^8.0.0", + "@twilio-paste/types": "^3.1.1", + "prop-types": "^15.7.2", + "react": "^16.8.6 || ^17.0.2", + "react-dom": "^16.8.6 || ^17.0.2" + }, + "devDependencies": { + "@twilio-paste/animation-library": "^0.3.2", + "@twilio-paste/box": "^7.0.0", + "@twilio-paste/customization": "^5.0.0", + "@twilio-paste/design-tokens": "^8.0.0", + "@twilio-paste/style-props": "^6.0.0", + "@twilio-paste/styling-library": "^1.0.0", + "@twilio-paste/theme": "^8.0.0", + "@twilio-paste/types": "^3.1.1", + "prop-types": "^15.7.2", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "typescript": "^4.9.4" + } +} diff --git a/packages/paste-core/components/detail-text/src/index.tsx b/packages/paste-core/components/detail-text/src/index.tsx new file mode 100644 index 0000000000..8fbba7ff25 --- /dev/null +++ b/packages/paste-core/components/detail-text/src/index.tsx @@ -0,0 +1,40 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import {Box, safelySpreadBoxProps} from '@twilio-paste/box'; +import type {BoxProps} from '@twilio-paste/box'; + +export interface DetailTextProps extends Omit, 'children'> { + children?: React.ReactNode; + element?: BoxProps['element']; + as?: BoxProps['as']; + marginTop?: 'space0' | 'space30'; +} + +export const DetailText = React.forwardRef( + ({element = 'DETAIL_TEXT', as = 'div', children, marginTop = 'space30', ...props}, ref) => { + return ( + + {children} + + ); + } +); + +DetailText.displayName = 'DetailText'; + +DetailText.propTypes = { + as: PropTypes.string as any, + children: PropTypes.node, + element: PropTypes.string, + marginTop: PropTypes.oneOf(['space0', 'space30']), +}; diff --git a/packages/paste-core/components/detail-text/stories/index.stories.tsx b/packages/paste-core/components/detail-text/stories/index.stories.tsx new file mode 100644 index 0000000000..4f7d043133 --- /dev/null +++ b/packages/paste-core/components/detail-text/stories/index.stories.tsx @@ -0,0 +1,47 @@ +import * as React from 'react'; +import type {StoryFn} from '@storybook/react'; +import {Box} from '@twilio-paste/box'; +import {CustomizationProvider} from '@twilio-paste/customization'; +import {useTheme} from '@twilio-paste/theme'; + +import {DetailText} from '../src'; + +// eslint-disable-next-line import/no-default-export +export default { + title: 'Components/Detail Text', + component: DetailText, +}; + +export const Default: StoryFn = () => ( + + + This is sample detail text. + +); +export const NoMargin: StoryFn = () => ( + + + This is sample detail text with no margin. + +); + +export const Customized: StoryFn = () => { + const theme = useTheme(); + + return ( + + + + This is customized detail text. + + + ); +}; diff --git a/packages/paste-core/components/detail-text/tsconfig.json b/packages/paste-core/components/detail-text/tsconfig.json new file mode 100644 index 0000000000..5e8a3b17a2 --- /dev/null +++ b/packages/paste-core/components/detail-text/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../../../tsconfig.json", + "compilerOptions": { + "outDir": "dist/" + }, + "include": ["src/**/*"], + "exclude": ["node_modules"] +} diff --git a/packages/paste-core/core-bundle/.gitignore b/packages/paste-core/core-bundle/.gitignore index f3d1f8b858..e13c13a10a 100644 --- a/packages/paste-core/core-bundle/.gitignore +++ b/packages/paste-core/core-bundle/.gitignore @@ -28,6 +28,7 @@ /date-picker /description-list /design-tokens +/detail-text /disclosure /disclosure-primitive /display-heading diff --git a/packages/paste-core/core-bundle/package.json b/packages/paste-core/core-bundle/package.json index db08c2f13f..a6f59eb445 100644 --- a/packages/paste-core/core-bundle/package.json +++ b/packages/paste-core/core-bundle/package.json @@ -52,6 +52,7 @@ "@twilio-paste/date-picker": "^3.0.2", "@twilio-paste/description-list": "^1.0.0", "@twilio-paste/design-tokens": "^8.5.0", + "@twilio-paste/detail-text": "^0.0.0", "@twilio-paste/disclosure": "^9.0.3", "@twilio-paste/disclosure-primitive": "^0.3.10", "@twilio-paste/display-heading": "^1.0.1", diff --git a/packages/paste-core/core-bundle/src/detail-text.tsx b/packages/paste-core/core-bundle/src/detail-text.tsx new file mode 100644 index 0000000000..0153e29e78 --- /dev/null +++ b/packages/paste-core/core-bundle/src/detail-text.tsx @@ -0,0 +1 @@ +export * from '@twilio-paste/detail-text'; diff --git a/packages/paste-core/core-bundle/src/index.tsx b/packages/paste-core/core-bundle/src/index.tsx index 1a71510b86..33e7d4cac9 100644 --- a/packages/paste-core/core-bundle/src/index.tsx +++ b/packages/paste-core/core-bundle/src/index.tsx @@ -20,6 +20,7 @@ export * from '@twilio-paste/combobox-primitive'; export * from '@twilio-paste/data-grid'; export * from '@twilio-paste/date-picker'; export * from '@twilio-paste/description-list'; +export * from '@twilio-paste/detail-text'; export * from '@twilio-paste/disclosure'; export * from '@twilio-paste/disclosure-primitive'; export * from '@twilio-paste/display-heading'; diff --git a/packages/paste-website/src/component-examples/DetailTextExamples.ts b/packages/paste-website/src/component-examples/DetailTextExamples.ts new file mode 100644 index 0000000000..3ff6b3ebb5 --- /dev/null +++ b/packages/paste-website/src/component-examples/DetailTextExamples.ts @@ -0,0 +1,158 @@ +export const detailTextWithImage = ` +const DetailTextExample = () => { + return ( + + + + Your sender identity is the “from” email address your recipients will see in their inbox. + + + ); +}; +render() +`.trim(); + +export const detailTextWithPrimaryContent = ` +const DetailTextExample = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + +
UserSkillsTasks
+ + Adam Brown + + adam@brown.com + English, French, Sales, Spanish35
+ + Adriana Lovelock + + adriana@lovelock.com + English, French, Sales, Spanish28
+ + Amanda Cutlack + + amanda@cutlack.com + English, French, Sales, Spanish7
+ ); +}; +render() +`.trim(); + +export const detailTextWithNoMargin = ` +const DetailTextExample = () => { + return ( + + + + Average handle time + + + + 3m + + + + + 8% + + + + Handle time by hour + + + + + Today + + + + Yesterday + + + + + ); +}; +render() +`.trim(); + +export const detailTextAsFootnote = ` +const DetailTextExample = () => { + return ( + <> + Auth tokens + + Auth tokens can be used to authenticate while making API requests. You will need to use HTTP Basic Authentication with user = Account SID and password = AuthToken. Auth tokens are specific to your account and can be used to access all API’s for the account. + + + *Please keep the auth tokens in a secure place and rotate them periodically. + + + ); +}; +render() +`.trim(); diff --git a/packages/paste-website/src/pages/components/detail-text/index.mdx b/packages/paste-website/src/pages/components/detail-text/index.mdx new file mode 100644 index 0000000000..5bf32d4af1 --- /dev/null +++ b/packages/paste-website/src/pages/components/detail-text/index.mdx @@ -0,0 +1,219 @@ +--- +title: Detail Text +package: '@twilio-paste/detail-text' +description: Detail Text is typography used for short pieces of secondary text content. +slug: /components/detail-text/ +--- + +import {graphql} from 'gatsby'; +import {DetailText} from '@twilio-paste/detail-text'; +import Changelog from '@twilio-paste/detail-text/CHANGELOG.md'; +import {Callout, CalloutText} from '@twilio-paste/callout'; +import {Box} from '@twilio-paste/box'; +import {Anchor} from '@twilio-paste/anchor'; +import {Heading} from '@twilio-paste/heading'; +import {Paragraph} from '@twilio-paste/paragraph'; +import {Table, THead, Tr, Th, Td, TBody} from '@twilio-paste/table'; +import {Text} from '@twilio-paste/text'; +import {ScreenReaderOnly} from '@twilio-paste/screen-reader-only'; +import {ArrowUpIcon} from '@twilio-paste/icons/esm/ArrowUpIcon'; +import {Card} from '@twilio-paste/card'; + +import {DoDont, Do, Dont} from '../../../components/DoDont'; +import {SidebarCategoryRoutes} from '../../../constants'; +import { + detailTextWithImage, + detailTextWithPrimaryContent, + detailTextWithNoMargin, + detailTextAsFootnote, +} from '../../../component-examples/DetailTextExamples.ts'; + +export const pageQuery = graphql` + { + allPasteComponent(filter: {name: {eq: "@twilio-paste/detail-text"}}) { + edges { + node { + name + description + status + version + } + } + } + mdx(frontmatter: {slug: {eq: "/components/detail-text/"}}) { + fileAbsolutePath + frontmatter { + slug + title + } + headings { + depth + value + } + } + allAirtable(filter: {data: {Feature: {eq: "Detail Text"}}}) { + edges { + node { + data { + Documentation + Figma + Design_committee_review + Engineer_committee_review + Code + status + } + } + } + } + } +`; + + + +--- + + + + + + + + + {detailTextWithImage} + + +## Guidelines + +### About Detail Text + +The Detail Text component is used for small pieces of text that are secondary to body text in the hierarchy of a page. Common examples include an image caption, chart element legend, or a paragraph footnote. + +Detail Text should not be used for body text, and should be reserved for small pieces of content only. It should not be used solely for its stylistic properties within other typographic elements. + +### Accessibility + +The Detail Text component is a `div` element by default. You can modify the element in situations where a `div` isn’t semantically correct. For example, set `as=”figcaption”` if working with Detail Text inside a [`figure` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure). + +If your Detail Text is used to describe another element, use an `id` for the Detail Text component and `aria-describedby` for the other element to make the connection clear. + + + + Detail Text is not the same as alt text on images. The context and content of your usage might mean including alt + text for further detail, or making alt text an empty string to prevent redundancy.{' '} + + Learn more about alt text and when to use it. + + + + + + +--- + +## Examples + +### Detail Text below an image + +Use Detail Text to add a caption below an image. + + + {detailTextWithImage} + + +### Detail Text below primary content + +Use Detail Text to add a description below primary content for extra context. + + + {detailTextWithPrimaryContent} + + +### Detail Text with no top margin + +Detail Text includes a marginTop prop to remove its default top margin of `space30`. + + + {detailTextWithNoMargin} + + +### Detail Text as a footnote + +Use Detail Text to add a footnote below a paragraph. + + + {detailTextAsFootnote} + + +--- + +## When to use Detail Text + + + + + + + + + + + +--- + +## Usage Guide + +### API + +#### Installation + +```bash +yarn add @twilio-paste/detail-text - or - yarn add @twilio-paste/core +``` + +#### Usage + +```jsx +import {DetailText} from '@twilio-paste/core/detail-text'; + +const Component = () => This is my detail text.; +``` + +### Detail Text props + +| Prop | Type | Description | Default | +| ---------- | ----------------------- | ----------------------------------------------------------------------------------------- | ------------- | +| as? | `string` | The HTML tag to render the Detail Text as | 'div' | +| marginTop? | `'space0' \| 'space30'` | The marginTop of the DetailText. Currently we only allow space0 to remove top margin. | 'space30' | +| children? | `React.ReactNode` | The content of the DetailText | | +| element? | `string` | Overrides the default element name to apply unique styles with the Customization Provider | 'DETAIL_TEXT' | + +## Figma + +### Usage + +Detail Text is available in the Paste Components Figma library. + +### Properties + +| Property | Variants | Description | Default | +| -------- | ----------- | ----------------------------------------------- | ------- | +| Margin | "Yes", "No" | If there is a margin on top of the Detail Text. | "Yes" | + + + + + + + + diff --git a/packages/paste-website/src/pages/components/table/index.mdx b/packages/paste-website/src/pages/components/table/index.mdx index 2f5cacef30..2367e7dd3e 100644 --- a/packages/paste-website/src/pages/components/table/index.mdx +++ b/packages/paste-website/src/pages/components/table/index.mdx @@ -22,6 +22,7 @@ import {InformationIcon} from '@twilio-paste/icons/esm/InformationIcon'; import {ProcessErrorIcon} from '@twilio-paste/icons/esm/ProcessErrorIcon'; import {ProcessWarningIcon} from '@twilio-paste/icons/esm/ProcessWarningIcon'; import {ProcessSuccessIcon} from '@twilio-paste/icons/esm/ProcessSuccessIcon'; +import {DetailText} from '@twilio-paste/detail-text'; import Changelog from '@twilio-paste/table/CHANGELOG.md'; import {Callout, CalloutHeading, CalloutText} from '@twilio-paste/callout'; import {DoDont, Do, Dont} from '../../../components/DoDont'; @@ -813,7 +814,7 @@ description text. In this example, the send time is not being compared across ro click rates are; instead, the send time is used to add additional detail that helps the user identify and understand data in an individual row. - + {` @@ -825,30 +826,30 @@ understand data in an individual row. diff --git a/packages/paste-website/static/images/detail-text/detail-text-image.png b/packages/paste-website/static/images/detail-text/detail-text-image.png new file mode 100644 index 0000000000..8b6fc36e92 Binary files /dev/null and b/packages/paste-website/static/images/detail-text/detail-text-image.png differ diff --git a/packages/paste-website/static/images/detail-text/graph.png b/packages/paste-website/static/images/detail-text/graph.png new file mode 100644 index 0000000000..344a3d89a3 Binary files /dev/null and b/packages/paste-website/static/images/detail-text/graph.png differ diff --git a/yarn.lock b/yarn.lock index f5218d0ac3..58d9e3f22d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12211,7 +12211,7 @@ __metadata: languageName: unknown linkType: soft -"@twilio-paste/box@^7.1.1, @twilio-paste/box@^7.1.2, @twilio-paste/box@workspace:packages/paste-core/primitives/box": +"@twilio-paste/box@^7.0.0, @twilio-paste/box@^7.1.1, @twilio-paste/box@^7.1.2, @twilio-paste/box@workspace:packages/paste-core/primitives/box": version: 0.0.0-use.local resolution: "@twilio-paste/box@workspace:packages/paste-core/primitives/box" dependencies: @@ -12730,6 +12730,7 @@ __metadata: "@twilio-paste/date-picker": ^3.0.2 "@twilio-paste/description-list": ^1.0.0 "@twilio-paste/design-tokens": ^8.5.0 + "@twilio-paste/detail-text": ^0.0.0 "@twilio-paste/disclosure": ^9.0.3 "@twilio-paste/disclosure-primitive": ^0.3.10 "@twilio-paste/display-heading": ^1.0.1 @@ -12811,7 +12812,7 @@ __metadata: languageName: unknown linkType: soft -"@twilio-paste/customization@^5.0.1, @twilio-paste/customization@^5.0.2, @twilio-paste/customization@workspace:packages/paste-customization": +"@twilio-paste/customization@^5.0.0, @twilio-paste/customization@^5.0.1, @twilio-paste/customization@^5.0.2, @twilio-paste/customization@workspace:packages/paste-customization": version: 0.0.0-use.local resolution: "@twilio-paste/customization@workspace:packages/paste-customization" dependencies: @@ -12979,7 +12980,7 @@ __metadata: languageName: unknown linkType: soft -"@twilio-paste/design-tokens@^8.1.2, @twilio-paste/design-tokens@^8.3.0, @twilio-paste/design-tokens@^8.4.0, @twilio-paste/design-tokens@^8.5.0, @twilio-paste/design-tokens@workspace:packages/paste-design-tokens": +"@twilio-paste/design-tokens@^8.0.0, @twilio-paste/design-tokens@^8.1.2, @twilio-paste/design-tokens@^8.3.0, @twilio-paste/design-tokens@^8.4.0, @twilio-paste/design-tokens@^8.5.0, @twilio-paste/design-tokens@workspace:packages/paste-design-tokens": version: 0.0.0-use.local resolution: "@twilio-paste/design-tokens@workspace:packages/paste-design-tokens" dependencies: @@ -12995,6 +12996,37 @@ __metadata: languageName: unknown linkType: soft +"@twilio-paste/detail-text@^0.0.0, @twilio-paste/detail-text@workspace:packages/paste-core/components/detail-text": + version: 0.0.0-use.local + resolution: "@twilio-paste/detail-text@workspace:packages/paste-core/components/detail-text" + dependencies: + "@twilio-paste/animation-library": ^0.3.2 + "@twilio-paste/box": ^7.0.0 + "@twilio-paste/customization": ^5.0.0 + "@twilio-paste/design-tokens": ^8.0.0 + "@twilio-paste/style-props": ^6.0.0 + "@twilio-paste/styling-library": ^1.0.0 + "@twilio-paste/theme": ^8.0.0 + "@twilio-paste/types": ^3.1.1 + prop-types: ^15.7.2 + react: ^17.0.2 + react-dom: ^17.0.2 + typescript: ^4.9.4 + peerDependencies: + "@twilio-paste/animation-library": ^0.3.2 + "@twilio-paste/box": ^7.0.0 + "@twilio-paste/customization": ^5.0.0 + "@twilio-paste/design-tokens": ^8.0.0 + "@twilio-paste/style-props": ^6.0.0 + "@twilio-paste/styling-library": ^1.0.0 + "@twilio-paste/theme": ^8.0.0 + "@twilio-paste/types": ^3.1.1 + prop-types: ^15.7.2 + react: ^16.8.6 || ^17.0.2 + react-dom: ^16.8.6 || ^17.0.2 + languageName: unknown + linkType: soft + "@twilio-paste/disclosure-primitive@^0.3.10, @twilio-paste/disclosure-primitive@workspace:packages/paste-core/primitives/disclosure": version: 0.0.0-use.local resolution: "@twilio-paste/disclosure-primitive@workspace:packages/paste-core/primitives/disclosure" @@ -14334,7 +14366,7 @@ __metadata: languageName: unknown linkType: soft -"@twilio-paste/style-props@^6.1.1, @twilio-paste/style-props@^6.1.2, @twilio-paste/style-props@workspace:packages/paste-style-props": +"@twilio-paste/style-props@^6.0.0, @twilio-paste/style-props@^6.1.1, @twilio-paste/style-props@^6.1.2, @twilio-paste/style-props@workspace:packages/paste-style-props": version: 0.0.0-use.local resolution: "@twilio-paste/style-props@workspace:packages/paste-style-props" dependencies:
- + Adam Brown - adam@brown.com + adam@brown.com English, French, Sales, Spanish 35
- + Adriana Lovelock - adriana@lovelock.com + adriana@lovelock.com English, French, Sales, Spanish 28
- + Amanda Cutlack - amanda@cutlack.com + amanda@cutlack.com English, French, Sales, Spanish 7