@sproutsocial/seeds-react-card 1.1.13 → 1.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,14 +8,14 @@ $ tsup --dts
8
8
  CLI Cleaning output folder
9
9
  CJS Build start
10
10
  ESM Build start
11
- CJS dist/index.js 10.86 KB
12
- CJS dist/index.js.map 21.11 KB
13
- CJS ⚡️ Build success in 104ms
14
- ESM dist/esm/index.js 7.83 KB
15
- ESM dist/esm/index.js.map 21.09 KB
16
- ESM ⚡️ Build success in 105ms
11
+ CJS dist/index.js 11.10 KB
12
+ CJS dist/index.js.map 21.91 KB
13
+ CJS ⚡️ Build success in 182ms
14
+ ESM dist/esm/index.js 8.06 KB
15
+ ESM dist/esm/index.js.map 21.89 KB
16
+ ESM ⚡️ Build success in 183ms
17
17
  DTS Build start
18
- DTS ⚡️ Build success in 11605ms
19
- DTS dist/index.d.ts 5.18 KB
20
- DTS dist/index.d.mts 5.18 KB
21
- Done in 13.92s.
18
+ DTS ⚡️ Build success in 21306ms
19
+ DTS dist/index.d.ts 5.60 KB
20
+ DTS dist/index.d.mts 5.60 KB
21
+ Done in 27.58s.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @sproutsocial/seeds-react-card
2
2
 
3
+ ## 1.1.14
4
+
5
+ ### Patch Changes
6
+
7
+ - 95d2b96: Remove interactivity support from presentation Cards for accessibility compliance. Cards with `role="presentation"` no longer support `onClick` handlers or keyboard interactions. Presentation Cards are now purely decorative containers outside the accessibility tree. Use `role="button"` for interactive Cards instead.
8
+
3
9
  ## 1.1.13
4
10
 
5
11
  ### Patch Changes
package/dist/esm/index.js CHANGED
@@ -100,15 +100,19 @@ var StyledCard = styled.div`
100
100
  &[role="button"],
101
101
  &[role="checkbox"] {
102
102
  cursor: pointer;
103
+
104
+ &:hover {
105
+ box-shadow: ${({ theme, $elevation = "low" }) => theme.shadows[$elevation]};
106
+ }
103
107
  }
104
108
 
105
- ${({ $isRoleLink }) => $isRoleLink && `
109
+ ${({ $isRoleLink, theme, $elevation = "low" }) => $isRoleLink && `
106
110
  cursor: pointer;
107
- `}
108
111
 
109
- &:hover {
110
- box-shadow: ${({ theme, $elevation = "low" }) => theme.shadows[$elevation]};
111
- }
112
+ &:hover {
113
+ box-shadow: ${theme.shadows[$elevation]};
114
+ }
115
+ `}
112
116
 
113
117
  &:focus-within {
114
118
  ${({ $isRoleLink }) => $isRoleLink ? focusRing : null}
@@ -276,6 +280,7 @@ var Card = ({
276
280
  const containerRef = useRef(null);
277
281
  const linkRef = useRef(null);
278
282
  const isRoleLink = role === "link";
283
+ const isRolePresentation = role === "presentation";
279
284
  const checkedConditions = role === "checkbox" ? selected : void 0;
280
285
  const cardContext = {
281
286
  setHasSubComponent,
@@ -287,10 +292,10 @@ var Card = ({
287
292
  return /* @__PURE__ */ jsxs2(
288
293
  StyledCard,
289
294
  {
290
- tabIndex: isRoleLink ? -1 : 0,
295
+ tabIndex: isRoleLink || isRolePresentation ? -1 : 0,
291
296
  role: isRoleLink ? void 0 : role,
292
- onClick: handleClickConditions,
293
- onKeyDown: handleKeyDown,
297
+ onClick: isRolePresentation ? void 0 : handleClickConditions,
298
+ onKeyDown: isRolePresentation ? void 0 : handleKeyDown,
294
299
  $elevation: elevation,
295
300
  ref: containerRef,
296
301
  $selected: selected,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/Card.tsx","../../src/styles.tsx","../../src/utils.ts","../../src/subComponents.tsx","../../src/CardTypes.ts","../../src/index.ts"],"sourcesContent":["import React, { useRef, useState } from \"react\";\nimport { StyledCard } from \"./styles\";\nimport type { TypeCardProps, TypeCardContext } from \"./CardTypes\";\nimport { SubComponentContext, onKeyDown } from \"./utils\";\nimport { SelectedIcon } from \"./subComponents\";\n\n/**\n * @link https://seeds.sproutsocial.com/components/card/\n *\n * Avoid nesting interactive content inside a Card with role='button'.\n *\n * Interactive content: \"a\", \"audio\", \"button\", \"embed\", \"iframe\", \"img\", \"input\", \"label\", \"select\", \"textarea\", \"video\"\n * @see https://html.spec.whatwg.org/multipage/dom.html#interactive-content\n *\n * @example\n * <Card role=\"button\" onClick={_onClick}>\n * <Button>Click me</Button>\n * </Card>\n */\n\nconst Card = ({\n children,\n disabled = false,\n elevation = \"low\",\n href,\n onClick,\n role = \"presentation\",\n selected,\n ...rest\n}: TypeCardProps) => {\n const [hasSubComponent, setHasSubComponent] = useState<boolean>(false);\n const containerRef = useRef<HTMLDivElement>(null);\n const linkRef = useRef<HTMLAnchorElement>(null);\n const isRoleLink = role === \"link\";\n const checkedConditions = role === \"checkbox\" ? selected : undefined;\n\n const cardContext: TypeCardContext = {\n setHasSubComponent: setHasSubComponent,\n href: href,\n linkRef: linkRef,\n };\n\n const handleClickConditions: React.MouseEventHandler = (e) =>\n isRoleLink ? linkRef.current?.click() : onClick?.(e);\n\n const handleKeyDown: React.KeyboardEventHandler<HTMLDivElement> = (e) =>\n onKeyDown({ e, href, onClick, ref: containerRef, role });\n\n return (\n <StyledCard\n tabIndex={isRoleLink ? -1 : 0}\n role={isRoleLink ? undefined : role}\n onClick={handleClickConditions}\n onKeyDown={handleKeyDown}\n $elevation={elevation}\n ref={containerRef}\n $selected={selected}\n aria-checked={checkedConditions}\n $disabled={disabled}\n aria-disabled={disabled && disabled}\n $compositionalComponents={hasSubComponent}\n $isRoleLink={isRoleLink}\n {...rest}\n >\n <SelectedIcon $selected={selected} />\n <SubComponentContext.Provider value={cardContext}>\n {children}\n </SubComponentContext.Provider>\n </StyledCard>\n );\n};\n\nexport default Card;\n","import styled from \"styled-components\";\nimport {\n border,\n color,\n flexbox,\n grid,\n layout,\n position,\n space,\n typography,\n} from \"styled-system\";\nimport { focusRing, disabled } from \"@sproutsocial/seeds-react-mixins\";\nimport type {\n TypeStyledCard,\n TypeCardArea,\n TypeStyledSelectedIcon,\n TypeCardLink,\n} from \"./CardTypes\";\nimport Icon from \"@sproutsocial/seeds-react-icon\";\n\n// TODO: Would be really cool to cherry pick specific props from style functions. For example,\n// removing the css prop 'color' from the color function or importing just the specific\n// props the component needs. It appears to be possible with some and not others.\n// https://github.com/styled-system/styled-system/issues/1569\n\nexport const StyledCardContent = styled.div<TypeCardArea>`\n display: flex;\n flex-direction: column;\n padding: ${({ theme }) => theme.space[400]};\n box-sizing: border-box;\n\n ${border}\n ${color}\n ${flexbox}\n ${grid}\n ${layout}\n ${space}\n`;\n\nexport const StyledCardHeader = styled(StyledCardContent)`\n flex-direction: row;\n border-bottom: ${({ theme }) => `${theme.borderWidths[500]} solid\n ${theme.colors.container.border.base}`};\n border-top-left-radius: ${({ theme }) => theme.radii.inner};\n border-top-right-radius: ${({ theme }) => theme.radii.inner};\n\n ${border}\n ${color}\n ${flexbox}\n ${grid}\n ${layout}\n ${space}\n`;\n\nexport const StyledCardFooter = styled(StyledCardContent)`\n flex-direction: row;\n border-top: ${({ theme }) => `${theme.borderWidths[500]} solid\n ${theme.colors.container.border.base}`};\n border-bottom-left-radius: ${({ theme }) => theme.radii.inner};\n border-bottom-right-radius: ${({ theme }) => theme.radii.inner};\n\n ${border}\n ${color}\n ${flexbox}\n ${grid}\n ${layout}\n ${space}\n`;\n\nexport const SelectedIconWrapper = styled.div`\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n top: -8px;\n right: -8px;\n`;\n\nexport const StyledSelectedIcon = styled(Icon)<TypeStyledSelectedIcon>`\n border-radius: 50%;\n background: ${({ theme }) => theme.colors.container.background.base};\n opacity: 0;\n transition: opacity ${({ theme }) => theme.duration.medium};\n\n ${({ $selected }) =>\n $selected &&\n `\n opacity: 1;\n `}\n`;\n\nexport const StyledCardLink = styled.a<TypeCardLink>`\n font-family: ${(p) => p.theme.fontFamily};\n font-weight: ${(p) => p.theme.fontWeights.bold};\n color: ${(p) => p.theme.colors.text.headline};\n ${(p) => p.theme.typography[400]};\n\n ${color}\n ${typography}\n`;\n\nexport const StyledCard = styled.div<TypeStyledCard>`\n position: relative;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n margin: 0;\n background: ${({ theme }) => theme.colors.container.background.base};\n border: ${({ theme }) => theme.borderWidths[500]} solid\n ${({ theme }) => theme.colors.container.border.base};\n padding: ${({ theme, $compositionalComponents }) =>\n $compositionalComponents ? 0 : theme.space[400]};\n border-radius: ${({ theme }) => theme.radii.outer};\n transition: box-shadow ${({ theme }) => theme.duration.medium},\n border ${({ theme }) => theme.duration.medium};\n\n &[role=\"button\"],\n &[role=\"checkbox\"] {\n cursor: pointer;\n }\n\n ${({ $isRoleLink }) =>\n $isRoleLink &&\n `\n\t\tcursor: pointer;\n\t`}\n\n &:hover {\n box-shadow: ${({ theme, $elevation = \"low\" }) => theme.shadows[$elevation]};\n }\n\n &:focus-within {\n ${({ $isRoleLink }) => ($isRoleLink ? focusRing : null)}\n ${StyledCardLink}:focus {\n border: none;\n box-shadow: none;\n outline: none;\n }\n }\n\n &:focus {\n ${focusRing}\n }\n\n ${({ $disabled }) =>\n $disabled &&\n `\n ${disabled}\n `}\n\n ${({ $selected, theme }) =>\n $selected &&\n `\n border: ${theme.borderWidths[500]} solid ${theme.colors.container.border.selected}; \n `}\n\n ${border}\n ${color}\n ${flexbox}\n ${grid}\n ${layout}\n ${position}\n ${space}\n`;\n\nexport const StyledCardAffordance = styled(Icon)`\n ${StyledCard}:hover & {\n transform: translateX(${(p) => p.theme.space[200]});\n }\n transition: ${(p) => p.theme.duration.medium};\n`;\n","import { createContext, useContext, useEffect } from \"react\";\nimport { assertIsElement } from \"@sproutsocial/seeds-react-utilities\";\nimport type { TypeCardProps, TypeCardContext } from \"./CardTypes\";\n\nexport const SubComponentContext = createContext<TypeCardContext>({\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n setHasSubComponent: () => {},\n href: \"\",\n linkRef: null,\n});\n\nexport function useChildContext() {\n const { setHasSubComponent } = useContext(SubComponentContext);\n useEffect(() => {\n setHasSubComponent && setHasSubComponent(true);\n }, [setHasSubComponent]);\n}\n\ninterface navigateToParams extends Pick<TypeCardProps, \"href\"> {\n e: React.MouseEvent | React.KeyboardEvent;\n ref: React.RefObject<HTMLDivElement>;\n}\n\nexport const navigateTo = ({ e, href, ref }: navigateToParams) => {\n const { target } = e;\n\n // asserts that target is an element so `contains` accepts it\n assertIsElement(target);\n\n if (ref.current?.contains(target)) {\n if (\n target.getAttribute(\"onclick\") !== null ||\n target.getAttribute(\"href\") !== null\n ) {\n e.stopPropagation();\n return;\n }\n }\n\n window.open(href, \"_blank\")?.focus();\n};\n\ninterface onKeyDownParams\n extends Pick<TypeCardProps, \"href\" | \"onClick\" | \"role\"> {\n e: React.KeyboardEvent;\n ref: React.RefObject<HTMLDivElement>;\n}\n\nexport const onKeyDown = ({ e, href, onClick, ref, role }: onKeyDownParams) => {\n if (e?.key === \"Enter\") {\n if (role === \"link\") {\n return navigateTo({ e, href, ref });\n }\n\n if (role === \"presentation\") {\n return;\n }\n\n return onClick?.(e);\n }\n};\n","import React, { useContext } from \"react\";\nimport { useChildContext, SubComponentContext } from \"./utils\";\nimport type {\n TypeCardLink,\n TypeSharedCardSystemProps,\n TypeStyledSelectedIcon,\n} from \"./CardTypes\";\nimport {\n StyledCardContent,\n StyledCardHeader,\n StyledCardFooter,\n StyledSelectedIcon,\n SelectedIconWrapper,\n StyledCardAffordance,\n StyledCardLink,\n} from \"./styles\";\n\ninterface TypeSharedSubComponentProps extends TypeSharedCardSystemProps {\n children?: React.ReactNode;\n}\n\nexport const CardContent = ({\n children,\n ...rest\n}: TypeSharedSubComponentProps) => {\n // TODO: It could be cool to possibly adjust the context to include an array of names of child components.\n // Then, if CardHeader or CardFooter aren't used with CardContent throw an error.\n useChildContext();\n return <StyledCardContent {...rest}>{children}</StyledCardContent>;\n};\n\nexport const CardHeader = ({\n children,\n ...rest\n}: TypeSharedSubComponentProps) => {\n useChildContext();\n return <StyledCardHeader {...rest}>{children}</StyledCardHeader>;\n};\n\nexport const CardFooter = ({\n children,\n ...rest\n}: TypeSharedSubComponentProps) => {\n useChildContext();\n return <StyledCardFooter {...rest}>{children}</StyledCardFooter>;\n};\n\ninterface TypeSelectedIconProps {\n $selected?: TypeStyledSelectedIcon[\"$selected\"];\n}\n\nexport const SelectedIcon = ({ $selected }: TypeSelectedIconProps) => {\n return (\n <SelectedIconWrapper>\n <StyledSelectedIcon\n aria-hidden\n color=\"icon.base\"\n name=\"circle-check-solid\"\n $selected={$selected}\n />\n </SelectedIconWrapper>\n );\n};\n\nexport const CardAffordance = ({ ...rest }) => {\n return (\n <StyledCardAffordance\n {...rest}\n size=\"mini\"\n name=\"arrow-right-solid\"\n // TODO: probably need to make this available to the top level for external links https://sprout.atlassian.net/browse/DS-2223\n aria-hidden\n />\n );\n};\n\nexport const CardLink = ({\n affordance,\n children,\n external = false,\n color,\n ...rest\n}: React.PropsWithChildren<TypeCardLink>) => {\n const { href, linkRef } = useContext(SubComponentContext);\n\n // Because we are hijacking Card click event to directly click this link, we need to stop propagation to avoid a double click event.\n const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {\n e.stopPropagation();\n };\n\n return (\n <StyledCardLink\n {...rest}\n target={external ? \"_blank\" : undefined}\n rel={external ? \"noreferrer\" : undefined}\n href={href}\n onClick={handleClick}\n ref={linkRef}\n // TODO: fix this type since `color` should be valid here. TS can't resolve the correct type.\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n color={color}\n >\n <>\n {children}\n {affordance ? <CardAffordance ml={300} /> : null}\n </>\n </StyledCardLink>\n );\n};\n","import type { TypeIconProps } from \"@sproutsocial/seeds-react-icon\";\nimport * as React from \"react\";\nimport type { TypeStyledComponentsCommonProps } from \"@sproutsocial/seeds-react-system-props\";\nimport type {\n TypeBorderSystemProps,\n TypeColorSystemProps,\n TypeFlexboxSystemProps,\n TypeGridSystemProps,\n TypeLayoutSystemProps,\n TypePositionSystemProps,\n TypeSpaceSystemProps,\n TypeTypographySystemProps,\n} from \"@sproutsocial/seeds-react-system-props\";\n\nexport interface TypeSharedCardSystemProps\n extends Omit<React.ComponentPropsWithoutRef<\"div\">, \"color\">,\n TypeStyledComponentsCommonProps,\n TypeBorderSystemProps,\n TypeColorSystemProps,\n TypeFlexboxSystemProps,\n TypeGridSystemProps,\n TypeLayoutSystemProps,\n TypePositionSystemProps,\n TypeSpaceSystemProps {}\n\n// consumer facing props that affect the styles of the component. We need to define these first so the user doesn't see our transient naming conventions.\nexport interface TypeCardStyleProps {\n elevation?: \"low\" | \"medium\" | \"high\";\n disabled?: boolean;\n compositionalComponents?: boolean;\n selected?: boolean;\n isRoleLink?: boolean;\n}\n\n// Since we only want to manage the style props in one place(above), we'll use this generic to prepend the properties of TypeCardStyleProps with $.\nexport type TypeStyleTransientProps<T> = {\n [K in Extract<keyof T, string> as `$${K}`]: T[K];\n};\n\nexport type TypeCardStyleTransientProps =\n TypeStyleTransientProps<TypeCardStyleProps>;\n\nexport interface TypeStyledCard\n extends TypeSharedCardSystemProps,\n TypeCardStyleTransientProps {}\n\nexport interface TypeCardStyles\n extends TypeSharedCardSystemProps,\n Omit<TypeCardStyleProps, \"compositionalComponents\"> {}\n\ntype TypeOnClick = (event: React.MouseEvent | React.KeyboardEvent) => void;\n\ntype TypeGenericCard = {\n /** role is used for to set accessibility properties,\n * to determine styling and interaction behavior,\n * and to determine which props should be allowed.*/\n role: \"link\" | \"button\" | \"checkbox\" | \"presentation\";\n /** When role is link, use href to determine the link destination.\n * Required for role=\"link\", disallowed for all other roles */\n href?: string;\n /** Required for role=\"button\" and role=\"checkbox\",\n * discouraged for role=\"presentation\", and disallowed for role=\"link\" */\n onClick?: TypeOnClick;\n /** Indicates whether the card is selected.\n * Required for role=\"checkbox\", disallowed for all other roles */\n selected?: boolean;\n};\n\nexport type TypeLinkCardProps = {\n role: \"link\";\n href: string;\n onClick?: never;\n selected?: never;\n};\n\nexport type TypeButtonCardProps = {\n role: \"button\";\n href?: never;\n onClick: TypeOnClick;\n selected?: never;\n};\n\nexport type TypeCheckboxCardProps = {\n role: \"checkbox\";\n href?: never;\n onClick: TypeOnClick;\n selected: boolean;\n};\n\nexport type TypePresentationCardProps = {\n role: \"presentation\";\n href?: never;\n /**\n * **Warning:**\n * `role='presentation'` is outside of the accessibility tree.\n * Using an `onClick` that performs a user action should likely be used\n * with `role='button'` instead.\n */\n onClick?: TypeOnClick;\n selected?: never;\n};\n\nexport type TypeCardConditions =\n | TypeLinkCardProps\n | TypeButtonCardProps\n | TypeCheckboxCardProps\n | TypePresentationCardProps;\n\nexport type TypeCardProps = React.PropsWithChildren<TypeCardStyles> &\n TypeGenericCard &\n TypeCardConditions;\n\nexport interface TypeCardArea extends TypeSharedCardSystemProps {\n $divider?: \"top\" | \"bottom\";\n}\n\nexport interface TypeStyledSelectedIcon extends TypeIconProps {\n $selected?: TypeCardStyleTransientProps[\"$selected\"];\n}\n\nexport interface TypeCardContext {\n setHasSubComponent?: React.Dispatch<React.SetStateAction<boolean>>;\n href?: string;\n linkRef: React.RefObject<HTMLAnchorElement> | null;\n}\n\nexport interface TypeCardLink\n extends Omit<React.ComponentPropsWithoutRef<\"a\">, \"color\">,\n TypeColorSystemProps,\n TypeTypographySystemProps {\n affordance?: boolean;\n external?: boolean;\n}\n","import Card from \"./Card\";\n\nexport default Card;\nexport { Card };\nexport { CardHeader, CardContent, CardFooter, CardLink } from \"./subComponents\";\nexport * from \"./CardTypes\";\n"],"mappings":"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","names":["useContext","color","useContext","jsx","jsxs","disabled"]}
1
+ {"version":3,"sources":["../../src/Card.tsx","../../src/styles.tsx","../../src/utils.ts","../../src/subComponents.tsx","../../src/CardTypes.ts","../../src/index.ts"],"sourcesContent":["import React, { useRef, useState } from \"react\";\nimport { StyledCard } from \"./styles\";\nimport type { TypeCardProps, TypeCardContext } from \"./CardTypes\";\nimport { SubComponentContext, onKeyDown } from \"./utils\";\nimport { SelectedIcon } from \"./subComponents\";\n\n/**\n * @link https://seeds.sproutsocial.com/components/card/\n *\n * Cards with role='presentation' are non-interactive for accessibility compliance.\n * Use role='button' for interactive Cards.\n *\n * Avoid nesting interactive content inside a Card with role='button'.\n *\n * Interactive content: \"a\", \"audio\", \"button\", \"embed\", \"iframe\", \"img\", \"input\", \"label\", \"select\", \"textarea\", \"video\"\n * @see https://html.spec.whatwg.org/multipage/dom.html#interactive-content\n *\n * @example\n * // Interactive Card\n * <Card role=\"button\" onClick={_onClick}>\n * <Text>Click this card</Text>\n * </Card>\n *\n * @example\n * // Presentation Card with interactive children\n * <Card role=\"presentation\">\n * <Text>This card is not interactive</Text>\n * <Link href=\"/details\">But this link is</Link>\n * </Card>\n */\n\nconst Card = ({\n children,\n disabled = false,\n elevation = \"low\",\n href,\n onClick,\n role = \"presentation\",\n selected,\n ...rest\n}: TypeCardProps) => {\n const [hasSubComponent, setHasSubComponent] = useState<boolean>(false);\n const containerRef = useRef<HTMLDivElement>(null);\n const linkRef = useRef<HTMLAnchorElement>(null);\n const isRoleLink = role === \"link\";\n const isRolePresentation = role === \"presentation\";\n const checkedConditions = role === \"checkbox\" ? selected : undefined;\n\n const cardContext: TypeCardContext = {\n setHasSubComponent: setHasSubComponent,\n href: href,\n linkRef: linkRef,\n };\n\n const handleClickConditions: React.MouseEventHandler = (e) =>\n isRoleLink ? linkRef.current?.click() : onClick?.(e);\n\n const handleKeyDown: React.KeyboardEventHandler<HTMLDivElement> = (e) =>\n onKeyDown({ e, href, onClick, ref: containerRef, role });\n\n return (\n <StyledCard\n tabIndex={isRoleLink || isRolePresentation ? -1 : 0}\n role={isRoleLink ? undefined : role}\n onClick={isRolePresentation ? undefined : handleClickConditions}\n onKeyDown={isRolePresentation ? undefined : handleKeyDown}\n $elevation={elevation}\n ref={containerRef}\n $selected={selected}\n aria-checked={checkedConditions}\n $disabled={disabled}\n aria-disabled={disabled && disabled}\n $compositionalComponents={hasSubComponent}\n $isRoleLink={isRoleLink}\n {...rest}\n >\n <SelectedIcon $selected={selected} />\n <SubComponentContext.Provider value={cardContext}>\n {children}\n </SubComponentContext.Provider>\n </StyledCard>\n );\n};\n\nexport default Card;\n","import styled from \"styled-components\";\nimport {\n border,\n color,\n flexbox,\n grid,\n layout,\n position,\n space,\n typography,\n} from \"styled-system\";\nimport { focusRing, disabled } from \"@sproutsocial/seeds-react-mixins\";\nimport type {\n TypeStyledCard,\n TypeCardArea,\n TypeStyledSelectedIcon,\n TypeCardLink,\n} from \"./CardTypes\";\nimport Icon from \"@sproutsocial/seeds-react-icon\";\n\n// TODO: Would be really cool to cherry pick specific props from style functions. For example,\n// removing the css prop 'color' from the color function or importing just the specific\n// props the component needs. It appears to be possible with some and not others.\n// https://github.com/styled-system/styled-system/issues/1569\n\nexport const StyledCardContent = styled.div<TypeCardArea>`\n display: flex;\n flex-direction: column;\n padding: ${({ theme }) => theme.space[400]};\n box-sizing: border-box;\n\n ${border}\n ${color}\n ${flexbox}\n ${grid}\n ${layout}\n ${space}\n`;\n\nexport const StyledCardHeader = styled(StyledCardContent)`\n flex-direction: row;\n border-bottom: ${({ theme }) => `${theme.borderWidths[500]} solid\n ${theme.colors.container.border.base}`};\n border-top-left-radius: ${({ theme }) => theme.radii.inner};\n border-top-right-radius: ${({ theme }) => theme.radii.inner};\n\n ${border}\n ${color}\n ${flexbox}\n ${grid}\n ${layout}\n ${space}\n`;\n\nexport const StyledCardFooter = styled(StyledCardContent)`\n flex-direction: row;\n border-top: ${({ theme }) => `${theme.borderWidths[500]} solid\n ${theme.colors.container.border.base}`};\n border-bottom-left-radius: ${({ theme }) => theme.radii.inner};\n border-bottom-right-radius: ${({ theme }) => theme.radii.inner};\n\n ${border}\n ${color}\n ${flexbox}\n ${grid}\n ${layout}\n ${space}\n`;\n\nexport const SelectedIconWrapper = styled.div`\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n top: -8px;\n right: -8px;\n`;\n\nexport const StyledSelectedIcon = styled(Icon)<TypeStyledSelectedIcon>`\n border-radius: 50%;\n background: ${({ theme }) => theme.colors.container.background.base};\n opacity: 0;\n transition: opacity ${({ theme }) => theme.duration.medium};\n\n ${({ $selected }) =>\n $selected &&\n `\n opacity: 1;\n `}\n`;\n\nexport const StyledCardLink = styled.a<TypeCardLink>`\n font-family: ${(p) => p.theme.fontFamily};\n font-weight: ${(p) => p.theme.fontWeights.bold};\n color: ${(p) => p.theme.colors.text.headline};\n ${(p) => p.theme.typography[400]};\n\n ${color}\n ${typography}\n`;\n\nexport const StyledCard = styled.div<TypeStyledCard>`\n position: relative;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n margin: 0;\n background: ${({ theme }) => theme.colors.container.background.base};\n border: ${({ theme }) => theme.borderWidths[500]} solid\n ${({ theme }) => theme.colors.container.border.base};\n padding: ${({ theme, $compositionalComponents }) =>\n $compositionalComponents ? 0 : theme.space[400]};\n border-radius: ${({ theme }) => theme.radii.outer};\n transition: box-shadow ${({ theme }) => theme.duration.medium},\n border ${({ theme }) => theme.duration.medium};\n\n &[role=\"button\"],\n &[role=\"checkbox\"] {\n cursor: pointer;\n\n &:hover {\n box-shadow: ${({ theme, $elevation = \"low\" }) =>\n theme.shadows[$elevation]};\n }\n }\n\n ${({ $isRoleLink, theme, $elevation = \"low\" }) =>\n $isRoleLink &&\n `\n\t\tcursor: pointer;\n\n &:hover {\n box-shadow: ${theme.shadows[$elevation]};\n }\n\t`}\n\n &:focus-within {\n ${({ $isRoleLink }) => ($isRoleLink ? focusRing : null)}\n ${StyledCardLink}:focus {\n border: none;\n box-shadow: none;\n outline: none;\n }\n }\n\n &:focus {\n ${focusRing}\n }\n\n ${({ $disabled }) =>\n $disabled &&\n `\n ${disabled}\n `}\n\n ${({ $selected, theme }) =>\n $selected &&\n `\n border: ${theme.borderWidths[500]} solid ${theme.colors.container.border.selected}; \n `}\n\n ${border}\n ${color}\n ${flexbox}\n ${grid}\n ${layout}\n ${position}\n ${space}\n`;\n\nexport const StyledCardAffordance = styled(Icon)`\n ${StyledCard}:hover & {\n transform: translateX(${(p) => p.theme.space[200]});\n }\n transition: ${(p) => p.theme.duration.medium};\n`;\n","import { createContext, useContext, useEffect } from \"react\";\nimport { assertIsElement } from \"@sproutsocial/seeds-react-utilities\";\nimport type { TypeCardProps, TypeCardContext } from \"./CardTypes\";\n\nexport const SubComponentContext = createContext<TypeCardContext>({\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n setHasSubComponent: () => {},\n href: \"\",\n linkRef: null,\n});\n\nexport function useChildContext() {\n const { setHasSubComponent } = useContext(SubComponentContext);\n useEffect(() => {\n setHasSubComponent && setHasSubComponent(true);\n }, [setHasSubComponent]);\n}\n\ninterface navigateToParams extends Pick<TypeCardProps, \"href\"> {\n e: React.MouseEvent | React.KeyboardEvent;\n ref: React.RefObject<HTMLDivElement>;\n}\n\nexport const navigateTo = ({ e, href, ref }: navigateToParams) => {\n const { target } = e;\n\n // asserts that target is an element so `contains` accepts it\n assertIsElement(target);\n\n if (ref.current?.contains(target)) {\n if (\n target.getAttribute(\"onclick\") !== null ||\n target.getAttribute(\"href\") !== null\n ) {\n e.stopPropagation();\n return;\n }\n }\n\n window.open(href, \"_blank\")?.focus();\n};\n\ninterface onKeyDownParams\n extends Pick<TypeCardProps, \"href\" | \"onClick\" | \"role\"> {\n e: React.KeyboardEvent;\n ref: React.RefObject<HTMLDivElement>;\n}\n\nexport const onKeyDown = ({ e, href, onClick, ref, role }: onKeyDownParams) => {\n if (e?.key === \"Enter\") {\n if (role === \"link\") {\n return navigateTo({ e, href, ref });\n }\n\n if (role === \"presentation\") {\n return;\n }\n\n return onClick?.(e);\n }\n};\n","import React, { useContext } from \"react\";\nimport { useChildContext, SubComponentContext } from \"./utils\";\nimport type {\n TypeCardLink,\n TypeSharedCardSystemProps,\n TypeStyledSelectedIcon,\n} from \"./CardTypes\";\nimport {\n StyledCardContent,\n StyledCardHeader,\n StyledCardFooter,\n StyledSelectedIcon,\n SelectedIconWrapper,\n StyledCardAffordance,\n StyledCardLink,\n} from \"./styles\";\n\ninterface TypeSharedSubComponentProps extends TypeSharedCardSystemProps {\n children?: React.ReactNode;\n}\n\nexport const CardContent = ({\n children,\n ...rest\n}: TypeSharedSubComponentProps) => {\n // TODO: It could be cool to possibly adjust the context to include an array of names of child components.\n // Then, if CardHeader or CardFooter aren't used with CardContent throw an error.\n useChildContext();\n return <StyledCardContent {...rest}>{children}</StyledCardContent>;\n};\n\nexport const CardHeader = ({\n children,\n ...rest\n}: TypeSharedSubComponentProps) => {\n useChildContext();\n return <StyledCardHeader {...rest}>{children}</StyledCardHeader>;\n};\n\nexport const CardFooter = ({\n children,\n ...rest\n}: TypeSharedSubComponentProps) => {\n useChildContext();\n return <StyledCardFooter {...rest}>{children}</StyledCardFooter>;\n};\n\ninterface TypeSelectedIconProps {\n $selected?: TypeStyledSelectedIcon[\"$selected\"];\n}\n\nexport const SelectedIcon = ({ $selected }: TypeSelectedIconProps) => {\n return (\n <SelectedIconWrapper>\n <StyledSelectedIcon\n aria-hidden\n color=\"icon.base\"\n name=\"circle-check-solid\"\n $selected={$selected}\n />\n </SelectedIconWrapper>\n );\n};\n\nexport const CardAffordance = ({ ...rest }) => {\n return (\n <StyledCardAffordance\n {...rest}\n size=\"mini\"\n name=\"arrow-right-solid\"\n // TODO: probably need to make this available to the top level for external links https://sprout.atlassian.net/browse/DS-2223\n aria-hidden\n />\n );\n};\n\nexport const CardLink = ({\n affordance,\n children,\n external = false,\n color,\n ...rest\n}: React.PropsWithChildren<TypeCardLink>) => {\n const { href, linkRef } = useContext(SubComponentContext);\n\n // Because we are hijacking Card click event to directly click this link, we need to stop propagation to avoid a double click event.\n const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {\n e.stopPropagation();\n };\n\n return (\n <StyledCardLink\n {...rest}\n target={external ? \"_blank\" : undefined}\n rel={external ? \"noreferrer\" : undefined}\n href={href}\n onClick={handleClick}\n ref={linkRef}\n // TODO: fix this type since `color` should be valid here. TS can't resolve the correct type.\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n color={color}\n >\n <>\n {children}\n {affordance ? <CardAffordance ml={300} /> : null}\n </>\n </StyledCardLink>\n );\n};\n","import type { TypeIconProps } from \"@sproutsocial/seeds-react-icon\";\nimport * as React from \"react\";\nimport type { TypeStyledComponentsCommonProps } from \"@sproutsocial/seeds-react-system-props\";\nimport type {\n TypeBorderSystemProps,\n TypeColorSystemProps,\n TypeFlexboxSystemProps,\n TypeGridSystemProps,\n TypeLayoutSystemProps,\n TypePositionSystemProps,\n TypeSpaceSystemProps,\n TypeTypographySystemProps,\n} from \"@sproutsocial/seeds-react-system-props\";\n\nexport interface TypeSharedCardSystemProps\n extends Omit<React.ComponentPropsWithoutRef<\"div\">, \"color\">,\n TypeStyledComponentsCommonProps,\n TypeBorderSystemProps,\n TypeColorSystemProps,\n TypeFlexboxSystemProps,\n TypeGridSystemProps,\n TypeLayoutSystemProps,\n TypePositionSystemProps,\n TypeSpaceSystemProps {}\n\n// consumer facing props that affect the styles of the component. We need to define these first so the user doesn't see our transient naming conventions.\nexport interface TypeCardStyleProps {\n elevation?: \"low\" | \"medium\" | \"high\";\n disabled?: boolean;\n compositionalComponents?: boolean;\n selected?: boolean;\n isRoleLink?: boolean;\n}\n\n// Since we only want to manage the style props in one place(above), we'll use this generic to prepend the properties of TypeCardStyleProps with $.\nexport type TypeStyleTransientProps<T> = {\n [K in Extract<keyof T, string> as `$${K}`]: T[K];\n};\n\nexport type TypeCardStyleTransientProps =\n TypeStyleTransientProps<TypeCardStyleProps>;\n\nexport interface TypeStyledCard\n extends TypeSharedCardSystemProps,\n TypeCardStyleTransientProps {}\n\nexport interface TypeCardStyles\n extends TypeSharedCardSystemProps,\n Omit<TypeCardStyleProps, \"compositionalComponents\"> {}\n\ntype TypeOnClick = (event: React.MouseEvent | React.KeyboardEvent) => void;\n\ntype TypeGenericCard = {\n /** role is used for to set accessibility properties,\n * to determine styling and interaction behavior,\n * and to determine which props should be allowed.*/\n role: \"link\" | \"button\" | \"checkbox\" | \"presentation\";\n /** When role is link, use href to determine the link destination.\n * Required for role=\"link\", disallowed for all other roles */\n href?: string;\n /** Required for role=\"button\" and role=\"checkbox\",\n * discouraged for role=\"presentation\", and disallowed for role=\"link\" */\n onClick?: TypeOnClick;\n /** Indicates whether the card is selected.\n * Required for role=\"checkbox\", disallowed for all other roles */\n selected?: boolean;\n};\n\nexport type TypeLinkCardProps = {\n role: \"link\";\n href: string;\n onClick?: never;\n selected?: never;\n};\n\nexport type TypeButtonCardProps = {\n role: \"button\";\n href?: never;\n onClick: TypeOnClick;\n selected?: never;\n};\n\nexport type TypeCheckboxCardProps = {\n role: \"checkbox\";\n href?: never;\n onClick: TypeOnClick;\n selected: boolean;\n};\n\nexport type TypePresentationCardProps = {\n role: \"presentation\";\n href?: never;\n /**\n * **Not supported for accessibility:**\n * `role='presentation'` removes the element from the accessibility tree.\n * Presentation Cards do not support `onClick` handlers or keyboard interactions.\n * Use `role='button'` for interactive Cards instead.\n */\n onClick?: never;\n selected?: never;\n};\n\nexport type TypeCardConditions =\n | TypeLinkCardProps\n | TypeButtonCardProps\n | TypeCheckboxCardProps\n | TypePresentationCardProps;\n\nexport type TypeCardProps = React.PropsWithChildren<TypeCardStyles> &\n TypeGenericCard &\n TypeCardConditions;\n\nexport interface TypeCardArea extends TypeSharedCardSystemProps {\n $divider?: \"top\" | \"bottom\";\n}\n\nexport interface TypeStyledSelectedIcon extends TypeIconProps {\n $selected?: TypeCardStyleTransientProps[\"$selected\"];\n}\n\nexport interface TypeCardContext {\n setHasSubComponent?: React.Dispatch<React.SetStateAction<boolean>>;\n href?: string;\n linkRef: React.RefObject<HTMLAnchorElement> | null;\n}\n\nexport interface TypeCardLink\n extends Omit<React.ComponentPropsWithoutRef<\"a\">, \"color\">,\n TypeColorSystemProps,\n TypeTypographySystemProps {\n affordance?: boolean;\n external?: boolean;\n}\n","import Card from \"./Card\";\n\nexport default Card;\nexport { Card };\nexport { CardHeader, CardContent, CardFooter, CardLink } from \"./subComponents\";\nexport * from \"./CardTypes\";\n"],"mappings":"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","names":["useContext","color","useContext","jsx","jsxs","disabled"]}
package/dist/index.d.mts CHANGED
@@ -59,12 +59,12 @@ type TypePresentationCardProps = {
59
59
  role: "presentation";
60
60
  href?: never;
61
61
  /**
62
- * **Warning:**
63
- * `role='presentation'` is outside of the accessibility tree.
64
- * Using an `onClick` that performs a user action should likely be used
65
- * with `role='button'` instead.
62
+ * **Not supported for accessibility:**
63
+ * `role='presentation'` removes the element from the accessibility tree.
64
+ * Presentation Cards do not support `onClick` handlers or keyboard interactions.
65
+ * Use `role='button'` for interactive Cards instead.
66
66
  */
67
- onClick?: TypeOnClick;
67
+ onClick?: never;
68
68
  selected?: never;
69
69
  };
70
70
  type TypeCardConditions = TypeLinkCardProps | TypeButtonCardProps | TypeCheckboxCardProps | TypePresentationCardProps;
@@ -88,14 +88,25 @@ interface TypeCardLink extends Omit<React.ComponentPropsWithoutRef<"a">, "color"
88
88
  /**
89
89
  * @link https://seeds.sproutsocial.com/components/card/
90
90
  *
91
+ * Cards with role='presentation' are non-interactive for accessibility compliance.
92
+ * Use role='button' for interactive Cards.
93
+ *
91
94
  * Avoid nesting interactive content inside a Card with role='button'.
92
95
  *
93
96
  * Interactive content: "a", "audio", "button", "embed", "iframe", "img", "input", "label", "select", "textarea", "video"
94
97
  * @see https://html.spec.whatwg.org/multipage/dom.html#interactive-content
95
98
  *
96
99
  * @example
100
+ * // Interactive Card
97
101
  * <Card role="button" onClick={_onClick}>
98
- * <Button>Click me</Button>
102
+ * <Text>Click this card</Text>
103
+ * </Card>
104
+ *
105
+ * @example
106
+ * // Presentation Card with interactive children
107
+ * <Card role="presentation">
108
+ * <Text>This card is not interactive</Text>
109
+ * <Link href="/details">But this link is</Link>
99
110
  * </Card>
100
111
  */
101
112
  declare const Card: ({ children, disabled, elevation, href, onClick, role, selected, ...rest }: TypeCardProps) => react_jsx_runtime.JSX.Element;
package/dist/index.d.ts CHANGED
@@ -59,12 +59,12 @@ type TypePresentationCardProps = {
59
59
  role: "presentation";
60
60
  href?: never;
61
61
  /**
62
- * **Warning:**
63
- * `role='presentation'` is outside of the accessibility tree.
64
- * Using an `onClick` that performs a user action should likely be used
65
- * with `role='button'` instead.
62
+ * **Not supported for accessibility:**
63
+ * `role='presentation'` removes the element from the accessibility tree.
64
+ * Presentation Cards do not support `onClick` handlers or keyboard interactions.
65
+ * Use `role='button'` for interactive Cards instead.
66
66
  */
67
- onClick?: TypeOnClick;
67
+ onClick?: never;
68
68
  selected?: never;
69
69
  };
70
70
  type TypeCardConditions = TypeLinkCardProps | TypeButtonCardProps | TypeCheckboxCardProps | TypePresentationCardProps;
@@ -88,14 +88,25 @@ interface TypeCardLink extends Omit<React.ComponentPropsWithoutRef<"a">, "color"
88
88
  /**
89
89
  * @link https://seeds.sproutsocial.com/components/card/
90
90
  *
91
+ * Cards with role='presentation' are non-interactive for accessibility compliance.
92
+ * Use role='button' for interactive Cards.
93
+ *
91
94
  * Avoid nesting interactive content inside a Card with role='button'.
92
95
  *
93
96
  * Interactive content: "a", "audio", "button", "embed", "iframe", "img", "input", "label", "select", "textarea", "video"
94
97
  * @see https://html.spec.whatwg.org/multipage/dom.html#interactive-content
95
98
  *
96
99
  * @example
100
+ * // Interactive Card
97
101
  * <Card role="button" onClick={_onClick}>
98
- * <Button>Click me</Button>
102
+ * <Text>Click this card</Text>
103
+ * </Card>
104
+ *
105
+ * @example
106
+ * // Presentation Card with interactive children
107
+ * <Card role="presentation">
108
+ * <Text>This card is not interactive</Text>
109
+ * <Link href="/details">But this link is</Link>
99
110
  * </Card>
100
111
  */
101
112
  declare const Card: ({ children, disabled, elevation, href, onClick, role, selected, ...rest }: TypeCardProps) => react_jsx_runtime.JSX.Element;
package/dist/index.js CHANGED
@@ -132,15 +132,19 @@ var StyledCard = import_styled_components.default.div`
132
132
  &[role="button"],
133
133
  &[role="checkbox"] {
134
134
  cursor: pointer;
135
+
136
+ &:hover {
137
+ box-shadow: ${({ theme, $elevation = "low" }) => theme.shadows[$elevation]};
138
+ }
135
139
  }
136
140
 
137
- ${({ $isRoleLink }) => $isRoleLink && `
141
+ ${({ $isRoleLink, theme, $elevation = "low" }) => $isRoleLink && `
138
142
  cursor: pointer;
139
- `}
140
143
 
141
- &:hover {
142
- box-shadow: ${({ theme, $elevation = "low" }) => theme.shadows[$elevation]};
143
- }
144
+ &:hover {
145
+ box-shadow: ${theme.shadows[$elevation]};
146
+ }
147
+ `}
144
148
 
145
149
  &:focus-within {
146
150
  ${({ $isRoleLink }) => $isRoleLink ? import_seeds_react_mixins.focusRing : null}
@@ -308,6 +312,7 @@ var Card = ({
308
312
  const containerRef = (0, import_react3.useRef)(null);
309
313
  const linkRef = (0, import_react3.useRef)(null);
310
314
  const isRoleLink = role === "link";
315
+ const isRolePresentation = role === "presentation";
311
316
  const checkedConditions = role === "checkbox" ? selected : void 0;
312
317
  const cardContext = {
313
318
  setHasSubComponent,
@@ -319,10 +324,10 @@ var Card = ({
319
324
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
320
325
  StyledCard,
321
326
  {
322
- tabIndex: isRoleLink ? -1 : 0,
327
+ tabIndex: isRoleLink || isRolePresentation ? -1 : 0,
323
328
  role: isRoleLink ? void 0 : role,
324
- onClick: handleClickConditions,
325
- onKeyDown: handleKeyDown,
329
+ onClick: isRolePresentation ? void 0 : handleClickConditions,
330
+ onKeyDown: isRolePresentation ? void 0 : handleKeyDown,
326
331
  $elevation: elevation,
327
332
  ref: containerRef,
328
333
  $selected: selected,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/Card.tsx","../src/styles.tsx","../src/utils.ts","../src/subComponents.tsx","../src/CardTypes.ts"],"sourcesContent":["import Card from \"./Card\";\n\nexport default Card;\nexport { Card };\nexport { CardHeader, CardContent, CardFooter, CardLink } from \"./subComponents\";\nexport * from \"./CardTypes\";\n","import React, { useRef, useState } from \"react\";\nimport { StyledCard } from \"./styles\";\nimport type { TypeCardProps, TypeCardContext } from \"./CardTypes\";\nimport { SubComponentContext, onKeyDown } from \"./utils\";\nimport { SelectedIcon } from \"./subComponents\";\n\n/**\n * @link https://seeds.sproutsocial.com/components/card/\n *\n * Avoid nesting interactive content inside a Card with role='button'.\n *\n * Interactive content: \"a\", \"audio\", \"button\", \"embed\", \"iframe\", \"img\", \"input\", \"label\", \"select\", \"textarea\", \"video\"\n * @see https://html.spec.whatwg.org/multipage/dom.html#interactive-content\n *\n * @example\n * <Card role=\"button\" onClick={_onClick}>\n * <Button>Click me</Button>\n * </Card>\n */\n\nconst Card = ({\n children,\n disabled = false,\n elevation = \"low\",\n href,\n onClick,\n role = \"presentation\",\n selected,\n ...rest\n}: TypeCardProps) => {\n const [hasSubComponent, setHasSubComponent] = useState<boolean>(false);\n const containerRef = useRef<HTMLDivElement>(null);\n const linkRef = useRef<HTMLAnchorElement>(null);\n const isRoleLink = role === \"link\";\n const checkedConditions = role === \"checkbox\" ? selected : undefined;\n\n const cardContext: TypeCardContext = {\n setHasSubComponent: setHasSubComponent,\n href: href,\n linkRef: linkRef,\n };\n\n const handleClickConditions: React.MouseEventHandler = (e) =>\n isRoleLink ? linkRef.current?.click() : onClick?.(e);\n\n const handleKeyDown: React.KeyboardEventHandler<HTMLDivElement> = (e) =>\n onKeyDown({ e, href, onClick, ref: containerRef, role });\n\n return (\n <StyledCard\n tabIndex={isRoleLink ? -1 : 0}\n role={isRoleLink ? undefined : role}\n onClick={handleClickConditions}\n onKeyDown={handleKeyDown}\n $elevation={elevation}\n ref={containerRef}\n $selected={selected}\n aria-checked={checkedConditions}\n $disabled={disabled}\n aria-disabled={disabled && disabled}\n $compositionalComponents={hasSubComponent}\n $isRoleLink={isRoleLink}\n {...rest}\n >\n <SelectedIcon $selected={selected} />\n <SubComponentContext.Provider value={cardContext}>\n {children}\n </SubComponentContext.Provider>\n </StyledCard>\n );\n};\n\nexport default Card;\n","import styled from \"styled-components\";\nimport {\n border,\n color,\n flexbox,\n grid,\n layout,\n position,\n space,\n typography,\n} from \"styled-system\";\nimport { focusRing, disabled } from \"@sproutsocial/seeds-react-mixins\";\nimport type {\n TypeStyledCard,\n TypeCardArea,\n TypeStyledSelectedIcon,\n TypeCardLink,\n} from \"./CardTypes\";\nimport Icon from \"@sproutsocial/seeds-react-icon\";\n\n// TODO: Would be really cool to cherry pick specific props from style functions. For example,\n// removing the css prop 'color' from the color function or importing just the specific\n// props the component needs. It appears to be possible with some and not others.\n// https://github.com/styled-system/styled-system/issues/1569\n\nexport const StyledCardContent = styled.div<TypeCardArea>`\n display: flex;\n flex-direction: column;\n padding: ${({ theme }) => theme.space[400]};\n box-sizing: border-box;\n\n ${border}\n ${color}\n ${flexbox}\n ${grid}\n ${layout}\n ${space}\n`;\n\nexport const StyledCardHeader = styled(StyledCardContent)`\n flex-direction: row;\n border-bottom: ${({ theme }) => `${theme.borderWidths[500]} solid\n ${theme.colors.container.border.base}`};\n border-top-left-radius: ${({ theme }) => theme.radii.inner};\n border-top-right-radius: ${({ theme }) => theme.radii.inner};\n\n ${border}\n ${color}\n ${flexbox}\n ${grid}\n ${layout}\n ${space}\n`;\n\nexport const StyledCardFooter = styled(StyledCardContent)`\n flex-direction: row;\n border-top: ${({ theme }) => `${theme.borderWidths[500]} solid\n ${theme.colors.container.border.base}`};\n border-bottom-left-radius: ${({ theme }) => theme.radii.inner};\n border-bottom-right-radius: ${({ theme }) => theme.radii.inner};\n\n ${border}\n ${color}\n ${flexbox}\n ${grid}\n ${layout}\n ${space}\n`;\n\nexport const SelectedIconWrapper = styled.div`\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n top: -8px;\n right: -8px;\n`;\n\nexport const StyledSelectedIcon = styled(Icon)<TypeStyledSelectedIcon>`\n border-radius: 50%;\n background: ${({ theme }) => theme.colors.container.background.base};\n opacity: 0;\n transition: opacity ${({ theme }) => theme.duration.medium};\n\n ${({ $selected }) =>\n $selected &&\n `\n opacity: 1;\n `}\n`;\n\nexport const StyledCardLink = styled.a<TypeCardLink>`\n font-family: ${(p) => p.theme.fontFamily};\n font-weight: ${(p) => p.theme.fontWeights.bold};\n color: ${(p) => p.theme.colors.text.headline};\n ${(p) => p.theme.typography[400]};\n\n ${color}\n ${typography}\n`;\n\nexport const StyledCard = styled.div<TypeStyledCard>`\n position: relative;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n margin: 0;\n background: ${({ theme }) => theme.colors.container.background.base};\n border: ${({ theme }) => theme.borderWidths[500]} solid\n ${({ theme }) => theme.colors.container.border.base};\n padding: ${({ theme, $compositionalComponents }) =>\n $compositionalComponents ? 0 : theme.space[400]};\n border-radius: ${({ theme }) => theme.radii.outer};\n transition: box-shadow ${({ theme }) => theme.duration.medium},\n border ${({ theme }) => theme.duration.medium};\n\n &[role=\"button\"],\n &[role=\"checkbox\"] {\n cursor: pointer;\n }\n\n ${({ $isRoleLink }) =>\n $isRoleLink &&\n `\n\t\tcursor: pointer;\n\t`}\n\n &:hover {\n box-shadow: ${({ theme, $elevation = \"low\" }) => theme.shadows[$elevation]};\n }\n\n &:focus-within {\n ${({ $isRoleLink }) => ($isRoleLink ? focusRing : null)}\n ${StyledCardLink}:focus {\n border: none;\n box-shadow: none;\n outline: none;\n }\n }\n\n &:focus {\n ${focusRing}\n }\n\n ${({ $disabled }) =>\n $disabled &&\n `\n ${disabled}\n `}\n\n ${({ $selected, theme }) =>\n $selected &&\n `\n border: ${theme.borderWidths[500]} solid ${theme.colors.container.border.selected}; \n `}\n\n ${border}\n ${color}\n ${flexbox}\n ${grid}\n ${layout}\n ${position}\n ${space}\n`;\n\nexport const StyledCardAffordance = styled(Icon)`\n ${StyledCard}:hover & {\n transform: translateX(${(p) => p.theme.space[200]});\n }\n transition: ${(p) => p.theme.duration.medium};\n`;\n","import { createContext, useContext, useEffect } from \"react\";\nimport { assertIsElement } from \"@sproutsocial/seeds-react-utilities\";\nimport type { TypeCardProps, TypeCardContext } from \"./CardTypes\";\n\nexport const SubComponentContext = createContext<TypeCardContext>({\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n setHasSubComponent: () => {},\n href: \"\",\n linkRef: null,\n});\n\nexport function useChildContext() {\n const { setHasSubComponent } = useContext(SubComponentContext);\n useEffect(() => {\n setHasSubComponent && setHasSubComponent(true);\n }, [setHasSubComponent]);\n}\n\ninterface navigateToParams extends Pick<TypeCardProps, \"href\"> {\n e: React.MouseEvent | React.KeyboardEvent;\n ref: React.RefObject<HTMLDivElement>;\n}\n\nexport const navigateTo = ({ e, href, ref }: navigateToParams) => {\n const { target } = e;\n\n // asserts that target is an element so `contains` accepts it\n assertIsElement(target);\n\n if (ref.current?.contains(target)) {\n if (\n target.getAttribute(\"onclick\") !== null ||\n target.getAttribute(\"href\") !== null\n ) {\n e.stopPropagation();\n return;\n }\n }\n\n window.open(href, \"_blank\")?.focus();\n};\n\ninterface onKeyDownParams\n extends Pick<TypeCardProps, \"href\" | \"onClick\" | \"role\"> {\n e: React.KeyboardEvent;\n ref: React.RefObject<HTMLDivElement>;\n}\n\nexport const onKeyDown = ({ e, href, onClick, ref, role }: onKeyDownParams) => {\n if (e?.key === \"Enter\") {\n if (role === \"link\") {\n return navigateTo({ e, href, ref });\n }\n\n if (role === \"presentation\") {\n return;\n }\n\n return onClick?.(e);\n }\n};\n","import React, { useContext } from \"react\";\nimport { useChildContext, SubComponentContext } from \"./utils\";\nimport type {\n TypeCardLink,\n TypeSharedCardSystemProps,\n TypeStyledSelectedIcon,\n} from \"./CardTypes\";\nimport {\n StyledCardContent,\n StyledCardHeader,\n StyledCardFooter,\n StyledSelectedIcon,\n SelectedIconWrapper,\n StyledCardAffordance,\n StyledCardLink,\n} from \"./styles\";\n\ninterface TypeSharedSubComponentProps extends TypeSharedCardSystemProps {\n children?: React.ReactNode;\n}\n\nexport const CardContent = ({\n children,\n ...rest\n}: TypeSharedSubComponentProps) => {\n // TODO: It could be cool to possibly adjust the context to include an array of names of child components.\n // Then, if CardHeader or CardFooter aren't used with CardContent throw an error.\n useChildContext();\n return <StyledCardContent {...rest}>{children}</StyledCardContent>;\n};\n\nexport const CardHeader = ({\n children,\n ...rest\n}: TypeSharedSubComponentProps) => {\n useChildContext();\n return <StyledCardHeader {...rest}>{children}</StyledCardHeader>;\n};\n\nexport const CardFooter = ({\n children,\n ...rest\n}: TypeSharedSubComponentProps) => {\n useChildContext();\n return <StyledCardFooter {...rest}>{children}</StyledCardFooter>;\n};\n\ninterface TypeSelectedIconProps {\n $selected?: TypeStyledSelectedIcon[\"$selected\"];\n}\n\nexport const SelectedIcon = ({ $selected }: TypeSelectedIconProps) => {\n return (\n <SelectedIconWrapper>\n <StyledSelectedIcon\n aria-hidden\n color=\"icon.base\"\n name=\"circle-check-solid\"\n $selected={$selected}\n />\n </SelectedIconWrapper>\n );\n};\n\nexport const CardAffordance = ({ ...rest }) => {\n return (\n <StyledCardAffordance\n {...rest}\n size=\"mini\"\n name=\"arrow-right-solid\"\n // TODO: probably need to make this available to the top level for external links https://sprout.atlassian.net/browse/DS-2223\n aria-hidden\n />\n );\n};\n\nexport const CardLink = ({\n affordance,\n children,\n external = false,\n color,\n ...rest\n}: React.PropsWithChildren<TypeCardLink>) => {\n const { href, linkRef } = useContext(SubComponentContext);\n\n // Because we are hijacking Card click event to directly click this link, we need to stop propagation to avoid a double click event.\n const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {\n e.stopPropagation();\n };\n\n return (\n <StyledCardLink\n {...rest}\n target={external ? \"_blank\" : undefined}\n rel={external ? \"noreferrer\" : undefined}\n href={href}\n onClick={handleClick}\n ref={linkRef}\n // TODO: fix this type since `color` should be valid here. TS can't resolve the correct type.\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n color={color}\n >\n <>\n {children}\n {affordance ? <CardAffordance ml={300} /> : null}\n </>\n </StyledCardLink>\n );\n};\n","import type { TypeIconProps } from \"@sproutsocial/seeds-react-icon\";\nimport * as React from \"react\";\nimport type { TypeStyledComponentsCommonProps } from \"@sproutsocial/seeds-react-system-props\";\nimport type {\n TypeBorderSystemProps,\n TypeColorSystemProps,\n TypeFlexboxSystemProps,\n TypeGridSystemProps,\n TypeLayoutSystemProps,\n TypePositionSystemProps,\n TypeSpaceSystemProps,\n TypeTypographySystemProps,\n} from \"@sproutsocial/seeds-react-system-props\";\n\nexport interface TypeSharedCardSystemProps\n extends Omit<React.ComponentPropsWithoutRef<\"div\">, \"color\">,\n TypeStyledComponentsCommonProps,\n TypeBorderSystemProps,\n TypeColorSystemProps,\n TypeFlexboxSystemProps,\n TypeGridSystemProps,\n TypeLayoutSystemProps,\n TypePositionSystemProps,\n TypeSpaceSystemProps {}\n\n// consumer facing props that affect the styles of the component. We need to define these first so the user doesn't see our transient naming conventions.\nexport interface TypeCardStyleProps {\n elevation?: \"low\" | \"medium\" | \"high\";\n disabled?: boolean;\n compositionalComponents?: boolean;\n selected?: boolean;\n isRoleLink?: boolean;\n}\n\n// Since we only want to manage the style props in one place(above), we'll use this generic to prepend the properties of TypeCardStyleProps with $.\nexport type TypeStyleTransientProps<T> = {\n [K in Extract<keyof T, string> as `$${K}`]: T[K];\n};\n\nexport type TypeCardStyleTransientProps =\n TypeStyleTransientProps<TypeCardStyleProps>;\n\nexport interface TypeStyledCard\n extends TypeSharedCardSystemProps,\n TypeCardStyleTransientProps {}\n\nexport interface TypeCardStyles\n extends TypeSharedCardSystemProps,\n Omit<TypeCardStyleProps, \"compositionalComponents\"> {}\n\ntype TypeOnClick = (event: React.MouseEvent | React.KeyboardEvent) => void;\n\ntype TypeGenericCard = {\n /** role is used for to set accessibility properties,\n * to determine styling and interaction behavior,\n * and to determine which props should be allowed.*/\n role: \"link\" | \"button\" | \"checkbox\" | \"presentation\";\n /** When role is link, use href to determine the link destination.\n * Required for role=\"link\", disallowed for all other roles */\n href?: string;\n /** Required for role=\"button\" and role=\"checkbox\",\n * discouraged for role=\"presentation\", and disallowed for role=\"link\" */\n onClick?: TypeOnClick;\n /** Indicates whether the card is selected.\n * Required for role=\"checkbox\", disallowed for all other roles */\n selected?: boolean;\n};\n\nexport type TypeLinkCardProps = {\n role: \"link\";\n href: string;\n onClick?: never;\n selected?: never;\n};\n\nexport type TypeButtonCardProps = {\n role: \"button\";\n href?: never;\n onClick: TypeOnClick;\n selected?: never;\n};\n\nexport type TypeCheckboxCardProps = {\n role: \"checkbox\";\n href?: never;\n onClick: TypeOnClick;\n selected: boolean;\n};\n\nexport type TypePresentationCardProps = {\n role: \"presentation\";\n href?: never;\n /**\n * **Warning:**\n * `role='presentation'` is outside of the accessibility tree.\n * Using an `onClick` that performs a user action should likely be used\n * with `role='button'` instead.\n */\n onClick?: TypeOnClick;\n selected?: never;\n};\n\nexport type TypeCardConditions =\n | TypeLinkCardProps\n | TypeButtonCardProps\n | TypeCheckboxCardProps\n | TypePresentationCardProps;\n\nexport type TypeCardProps = React.PropsWithChildren<TypeCardStyles> &\n TypeGenericCard &\n TypeCardConditions;\n\nexport interface TypeCardArea extends TypeSharedCardSystemProps {\n $divider?: \"top\" | \"bottom\";\n}\n\nexport interface TypeStyledSelectedIcon extends TypeIconProps {\n $selected?: TypeCardStyleTransientProps[\"$selected\"];\n}\n\nexport interface TypeCardContext {\n setHasSubComponent?: React.Dispatch<React.SetStateAction<boolean>>;\n href?: string;\n linkRef: React.RefObject<HTMLAnchorElement> | null;\n}\n\nexport interface TypeCardLink\n extends Omit<React.ComponentPropsWithoutRef<\"a\">, \"color\">,\n TypeColorSystemProps,\n TypeTypographySystemProps {\n affordance?: boolean;\n external?: boolean;\n}\n"],"mappings":"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","names":["import_react","styled","Icon","import_react","color","import_jsx_runtime","disabled","React"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/Card.tsx","../src/styles.tsx","../src/utils.ts","../src/subComponents.tsx","../src/CardTypes.ts"],"sourcesContent":["import Card from \"./Card\";\n\nexport default Card;\nexport { Card };\nexport { CardHeader, CardContent, CardFooter, CardLink } from \"./subComponents\";\nexport * from \"./CardTypes\";\n","import React, { useRef, useState } from \"react\";\nimport { StyledCard } from \"./styles\";\nimport type { TypeCardProps, TypeCardContext } from \"./CardTypes\";\nimport { SubComponentContext, onKeyDown } from \"./utils\";\nimport { SelectedIcon } from \"./subComponents\";\n\n/**\n * @link https://seeds.sproutsocial.com/components/card/\n *\n * Cards with role='presentation' are non-interactive for accessibility compliance.\n * Use role='button' for interactive Cards.\n *\n * Avoid nesting interactive content inside a Card with role='button'.\n *\n * Interactive content: \"a\", \"audio\", \"button\", \"embed\", \"iframe\", \"img\", \"input\", \"label\", \"select\", \"textarea\", \"video\"\n * @see https://html.spec.whatwg.org/multipage/dom.html#interactive-content\n *\n * @example\n * // Interactive Card\n * <Card role=\"button\" onClick={_onClick}>\n * <Text>Click this card</Text>\n * </Card>\n *\n * @example\n * // Presentation Card with interactive children\n * <Card role=\"presentation\">\n * <Text>This card is not interactive</Text>\n * <Link href=\"/details\">But this link is</Link>\n * </Card>\n */\n\nconst Card = ({\n children,\n disabled = false,\n elevation = \"low\",\n href,\n onClick,\n role = \"presentation\",\n selected,\n ...rest\n}: TypeCardProps) => {\n const [hasSubComponent, setHasSubComponent] = useState<boolean>(false);\n const containerRef = useRef<HTMLDivElement>(null);\n const linkRef = useRef<HTMLAnchorElement>(null);\n const isRoleLink = role === \"link\";\n const isRolePresentation = role === \"presentation\";\n const checkedConditions = role === \"checkbox\" ? selected : undefined;\n\n const cardContext: TypeCardContext = {\n setHasSubComponent: setHasSubComponent,\n href: href,\n linkRef: linkRef,\n };\n\n const handleClickConditions: React.MouseEventHandler = (e) =>\n isRoleLink ? linkRef.current?.click() : onClick?.(e);\n\n const handleKeyDown: React.KeyboardEventHandler<HTMLDivElement> = (e) =>\n onKeyDown({ e, href, onClick, ref: containerRef, role });\n\n return (\n <StyledCard\n tabIndex={isRoleLink || isRolePresentation ? -1 : 0}\n role={isRoleLink ? undefined : role}\n onClick={isRolePresentation ? undefined : handleClickConditions}\n onKeyDown={isRolePresentation ? undefined : handleKeyDown}\n $elevation={elevation}\n ref={containerRef}\n $selected={selected}\n aria-checked={checkedConditions}\n $disabled={disabled}\n aria-disabled={disabled && disabled}\n $compositionalComponents={hasSubComponent}\n $isRoleLink={isRoleLink}\n {...rest}\n >\n <SelectedIcon $selected={selected} />\n <SubComponentContext.Provider value={cardContext}>\n {children}\n </SubComponentContext.Provider>\n </StyledCard>\n );\n};\n\nexport default Card;\n","import styled from \"styled-components\";\nimport {\n border,\n color,\n flexbox,\n grid,\n layout,\n position,\n space,\n typography,\n} from \"styled-system\";\nimport { focusRing, disabled } from \"@sproutsocial/seeds-react-mixins\";\nimport type {\n TypeStyledCard,\n TypeCardArea,\n TypeStyledSelectedIcon,\n TypeCardLink,\n} from \"./CardTypes\";\nimport Icon from \"@sproutsocial/seeds-react-icon\";\n\n// TODO: Would be really cool to cherry pick specific props from style functions. For example,\n// removing the css prop 'color' from the color function or importing just the specific\n// props the component needs. It appears to be possible with some and not others.\n// https://github.com/styled-system/styled-system/issues/1569\n\nexport const StyledCardContent = styled.div<TypeCardArea>`\n display: flex;\n flex-direction: column;\n padding: ${({ theme }) => theme.space[400]};\n box-sizing: border-box;\n\n ${border}\n ${color}\n ${flexbox}\n ${grid}\n ${layout}\n ${space}\n`;\n\nexport const StyledCardHeader = styled(StyledCardContent)`\n flex-direction: row;\n border-bottom: ${({ theme }) => `${theme.borderWidths[500]} solid\n ${theme.colors.container.border.base}`};\n border-top-left-radius: ${({ theme }) => theme.radii.inner};\n border-top-right-radius: ${({ theme }) => theme.radii.inner};\n\n ${border}\n ${color}\n ${flexbox}\n ${grid}\n ${layout}\n ${space}\n`;\n\nexport const StyledCardFooter = styled(StyledCardContent)`\n flex-direction: row;\n border-top: ${({ theme }) => `${theme.borderWidths[500]} solid\n ${theme.colors.container.border.base}`};\n border-bottom-left-radius: ${({ theme }) => theme.radii.inner};\n border-bottom-right-radius: ${({ theme }) => theme.radii.inner};\n\n ${border}\n ${color}\n ${flexbox}\n ${grid}\n ${layout}\n ${space}\n`;\n\nexport const SelectedIconWrapper = styled.div`\n display: flex;\n align-items: center;\n justify-content: center;\n position: absolute;\n top: -8px;\n right: -8px;\n`;\n\nexport const StyledSelectedIcon = styled(Icon)<TypeStyledSelectedIcon>`\n border-radius: 50%;\n background: ${({ theme }) => theme.colors.container.background.base};\n opacity: 0;\n transition: opacity ${({ theme }) => theme.duration.medium};\n\n ${({ $selected }) =>\n $selected &&\n `\n opacity: 1;\n `}\n`;\n\nexport const StyledCardLink = styled.a<TypeCardLink>`\n font-family: ${(p) => p.theme.fontFamily};\n font-weight: ${(p) => p.theme.fontWeights.bold};\n color: ${(p) => p.theme.colors.text.headline};\n ${(p) => p.theme.typography[400]};\n\n ${color}\n ${typography}\n`;\n\nexport const StyledCard = styled.div<TypeStyledCard>`\n position: relative;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n margin: 0;\n background: ${({ theme }) => theme.colors.container.background.base};\n border: ${({ theme }) => theme.borderWidths[500]} solid\n ${({ theme }) => theme.colors.container.border.base};\n padding: ${({ theme, $compositionalComponents }) =>\n $compositionalComponents ? 0 : theme.space[400]};\n border-radius: ${({ theme }) => theme.radii.outer};\n transition: box-shadow ${({ theme }) => theme.duration.medium},\n border ${({ theme }) => theme.duration.medium};\n\n &[role=\"button\"],\n &[role=\"checkbox\"] {\n cursor: pointer;\n\n &:hover {\n box-shadow: ${({ theme, $elevation = \"low\" }) =>\n theme.shadows[$elevation]};\n }\n }\n\n ${({ $isRoleLink, theme, $elevation = \"low\" }) =>\n $isRoleLink &&\n `\n\t\tcursor: pointer;\n\n &:hover {\n box-shadow: ${theme.shadows[$elevation]};\n }\n\t`}\n\n &:focus-within {\n ${({ $isRoleLink }) => ($isRoleLink ? focusRing : null)}\n ${StyledCardLink}:focus {\n border: none;\n box-shadow: none;\n outline: none;\n }\n }\n\n &:focus {\n ${focusRing}\n }\n\n ${({ $disabled }) =>\n $disabled &&\n `\n ${disabled}\n `}\n\n ${({ $selected, theme }) =>\n $selected &&\n `\n border: ${theme.borderWidths[500]} solid ${theme.colors.container.border.selected}; \n `}\n\n ${border}\n ${color}\n ${flexbox}\n ${grid}\n ${layout}\n ${position}\n ${space}\n`;\n\nexport const StyledCardAffordance = styled(Icon)`\n ${StyledCard}:hover & {\n transform: translateX(${(p) => p.theme.space[200]});\n }\n transition: ${(p) => p.theme.duration.medium};\n`;\n","import { createContext, useContext, useEffect } from \"react\";\nimport { assertIsElement } from \"@sproutsocial/seeds-react-utilities\";\nimport type { TypeCardProps, TypeCardContext } from \"./CardTypes\";\n\nexport const SubComponentContext = createContext<TypeCardContext>({\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n setHasSubComponent: () => {},\n href: \"\",\n linkRef: null,\n});\n\nexport function useChildContext() {\n const { setHasSubComponent } = useContext(SubComponentContext);\n useEffect(() => {\n setHasSubComponent && setHasSubComponent(true);\n }, [setHasSubComponent]);\n}\n\ninterface navigateToParams extends Pick<TypeCardProps, \"href\"> {\n e: React.MouseEvent | React.KeyboardEvent;\n ref: React.RefObject<HTMLDivElement>;\n}\n\nexport const navigateTo = ({ e, href, ref }: navigateToParams) => {\n const { target } = e;\n\n // asserts that target is an element so `contains` accepts it\n assertIsElement(target);\n\n if (ref.current?.contains(target)) {\n if (\n target.getAttribute(\"onclick\") !== null ||\n target.getAttribute(\"href\") !== null\n ) {\n e.stopPropagation();\n return;\n }\n }\n\n window.open(href, \"_blank\")?.focus();\n};\n\ninterface onKeyDownParams\n extends Pick<TypeCardProps, \"href\" | \"onClick\" | \"role\"> {\n e: React.KeyboardEvent;\n ref: React.RefObject<HTMLDivElement>;\n}\n\nexport const onKeyDown = ({ e, href, onClick, ref, role }: onKeyDownParams) => {\n if (e?.key === \"Enter\") {\n if (role === \"link\") {\n return navigateTo({ e, href, ref });\n }\n\n if (role === \"presentation\") {\n return;\n }\n\n return onClick?.(e);\n }\n};\n","import React, { useContext } from \"react\";\nimport { useChildContext, SubComponentContext } from \"./utils\";\nimport type {\n TypeCardLink,\n TypeSharedCardSystemProps,\n TypeStyledSelectedIcon,\n} from \"./CardTypes\";\nimport {\n StyledCardContent,\n StyledCardHeader,\n StyledCardFooter,\n StyledSelectedIcon,\n SelectedIconWrapper,\n StyledCardAffordance,\n StyledCardLink,\n} from \"./styles\";\n\ninterface TypeSharedSubComponentProps extends TypeSharedCardSystemProps {\n children?: React.ReactNode;\n}\n\nexport const CardContent = ({\n children,\n ...rest\n}: TypeSharedSubComponentProps) => {\n // TODO: It could be cool to possibly adjust the context to include an array of names of child components.\n // Then, if CardHeader or CardFooter aren't used with CardContent throw an error.\n useChildContext();\n return <StyledCardContent {...rest}>{children}</StyledCardContent>;\n};\n\nexport const CardHeader = ({\n children,\n ...rest\n}: TypeSharedSubComponentProps) => {\n useChildContext();\n return <StyledCardHeader {...rest}>{children}</StyledCardHeader>;\n};\n\nexport const CardFooter = ({\n children,\n ...rest\n}: TypeSharedSubComponentProps) => {\n useChildContext();\n return <StyledCardFooter {...rest}>{children}</StyledCardFooter>;\n};\n\ninterface TypeSelectedIconProps {\n $selected?: TypeStyledSelectedIcon[\"$selected\"];\n}\n\nexport const SelectedIcon = ({ $selected }: TypeSelectedIconProps) => {\n return (\n <SelectedIconWrapper>\n <StyledSelectedIcon\n aria-hidden\n color=\"icon.base\"\n name=\"circle-check-solid\"\n $selected={$selected}\n />\n </SelectedIconWrapper>\n );\n};\n\nexport const CardAffordance = ({ ...rest }) => {\n return (\n <StyledCardAffordance\n {...rest}\n size=\"mini\"\n name=\"arrow-right-solid\"\n // TODO: probably need to make this available to the top level for external links https://sprout.atlassian.net/browse/DS-2223\n aria-hidden\n />\n );\n};\n\nexport const CardLink = ({\n affordance,\n children,\n external = false,\n color,\n ...rest\n}: React.PropsWithChildren<TypeCardLink>) => {\n const { href, linkRef } = useContext(SubComponentContext);\n\n // Because we are hijacking Card click event to directly click this link, we need to stop propagation to avoid a double click event.\n const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {\n e.stopPropagation();\n };\n\n return (\n <StyledCardLink\n {...rest}\n target={external ? \"_blank\" : undefined}\n rel={external ? \"noreferrer\" : undefined}\n href={href}\n onClick={handleClick}\n ref={linkRef}\n // TODO: fix this type since `color` should be valid here. TS can't resolve the correct type.\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n color={color}\n >\n <>\n {children}\n {affordance ? <CardAffordance ml={300} /> : null}\n </>\n </StyledCardLink>\n );\n};\n","import type { TypeIconProps } from \"@sproutsocial/seeds-react-icon\";\nimport * as React from \"react\";\nimport type { TypeStyledComponentsCommonProps } from \"@sproutsocial/seeds-react-system-props\";\nimport type {\n TypeBorderSystemProps,\n TypeColorSystemProps,\n TypeFlexboxSystemProps,\n TypeGridSystemProps,\n TypeLayoutSystemProps,\n TypePositionSystemProps,\n TypeSpaceSystemProps,\n TypeTypographySystemProps,\n} from \"@sproutsocial/seeds-react-system-props\";\n\nexport interface TypeSharedCardSystemProps\n extends Omit<React.ComponentPropsWithoutRef<\"div\">, \"color\">,\n TypeStyledComponentsCommonProps,\n TypeBorderSystemProps,\n TypeColorSystemProps,\n TypeFlexboxSystemProps,\n TypeGridSystemProps,\n TypeLayoutSystemProps,\n TypePositionSystemProps,\n TypeSpaceSystemProps {}\n\n// consumer facing props that affect the styles of the component. We need to define these first so the user doesn't see our transient naming conventions.\nexport interface TypeCardStyleProps {\n elevation?: \"low\" | \"medium\" | \"high\";\n disabled?: boolean;\n compositionalComponents?: boolean;\n selected?: boolean;\n isRoleLink?: boolean;\n}\n\n// Since we only want to manage the style props in one place(above), we'll use this generic to prepend the properties of TypeCardStyleProps with $.\nexport type TypeStyleTransientProps<T> = {\n [K in Extract<keyof T, string> as `$${K}`]: T[K];\n};\n\nexport type TypeCardStyleTransientProps =\n TypeStyleTransientProps<TypeCardStyleProps>;\n\nexport interface TypeStyledCard\n extends TypeSharedCardSystemProps,\n TypeCardStyleTransientProps {}\n\nexport interface TypeCardStyles\n extends TypeSharedCardSystemProps,\n Omit<TypeCardStyleProps, \"compositionalComponents\"> {}\n\ntype TypeOnClick = (event: React.MouseEvent | React.KeyboardEvent) => void;\n\ntype TypeGenericCard = {\n /** role is used for to set accessibility properties,\n * to determine styling and interaction behavior,\n * and to determine which props should be allowed.*/\n role: \"link\" | \"button\" | \"checkbox\" | \"presentation\";\n /** When role is link, use href to determine the link destination.\n * Required for role=\"link\", disallowed for all other roles */\n href?: string;\n /** Required for role=\"button\" and role=\"checkbox\",\n * discouraged for role=\"presentation\", and disallowed for role=\"link\" */\n onClick?: TypeOnClick;\n /** Indicates whether the card is selected.\n * Required for role=\"checkbox\", disallowed for all other roles */\n selected?: boolean;\n};\n\nexport type TypeLinkCardProps = {\n role: \"link\";\n href: string;\n onClick?: never;\n selected?: never;\n};\n\nexport type TypeButtonCardProps = {\n role: \"button\";\n href?: never;\n onClick: TypeOnClick;\n selected?: never;\n};\n\nexport type TypeCheckboxCardProps = {\n role: \"checkbox\";\n href?: never;\n onClick: TypeOnClick;\n selected: boolean;\n};\n\nexport type TypePresentationCardProps = {\n role: \"presentation\";\n href?: never;\n /**\n * **Not supported for accessibility:**\n * `role='presentation'` removes the element from the accessibility tree.\n * Presentation Cards do not support `onClick` handlers or keyboard interactions.\n * Use `role='button'` for interactive Cards instead.\n */\n onClick?: never;\n selected?: never;\n};\n\nexport type TypeCardConditions =\n | TypeLinkCardProps\n | TypeButtonCardProps\n | TypeCheckboxCardProps\n | TypePresentationCardProps;\n\nexport type TypeCardProps = React.PropsWithChildren<TypeCardStyles> &\n TypeGenericCard &\n TypeCardConditions;\n\nexport interface TypeCardArea extends TypeSharedCardSystemProps {\n $divider?: \"top\" | \"bottom\";\n}\n\nexport interface TypeStyledSelectedIcon extends TypeIconProps {\n $selected?: TypeCardStyleTransientProps[\"$selected\"];\n}\n\nexport interface TypeCardContext {\n setHasSubComponent?: React.Dispatch<React.SetStateAction<boolean>>;\n href?: string;\n linkRef: React.RefObject<HTMLAnchorElement> | null;\n}\n\nexport interface TypeCardLink\n extends Omit<React.ComponentPropsWithoutRef<\"a\">, \"color\">,\n TypeColorSystemProps,\n TypeTypographySystemProps {\n affordance?: boolean;\n external?: boolean;\n}\n"],"mappings":"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","names":["import_react","styled","Icon","import_react","color","import_jsx_runtime","disabled","React"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sproutsocial/seeds-react-card",
3
- "version": "1.1.13",
3
+ "version": "1.1.14",
4
4
  "description": "Seeds React Card",
5
5
  "author": "Sprout Social, Inc.",
6
6
  "license": "MIT",
package/src/Card.tsx CHANGED
@@ -7,14 +7,25 @@ import { SelectedIcon } from "./subComponents";
7
7
  /**
8
8
  * @link https://seeds.sproutsocial.com/components/card/
9
9
  *
10
+ * Cards with role='presentation' are non-interactive for accessibility compliance.
11
+ * Use role='button' for interactive Cards.
12
+ *
10
13
  * Avoid nesting interactive content inside a Card with role='button'.
11
14
  *
12
15
  * Interactive content: "a", "audio", "button", "embed", "iframe", "img", "input", "label", "select", "textarea", "video"
13
16
  * @see https://html.spec.whatwg.org/multipage/dom.html#interactive-content
14
17
  *
15
18
  * @example
19
+ * // Interactive Card
16
20
  * <Card role="button" onClick={_onClick}>
17
- * <Button>Click me</Button>
21
+ * <Text>Click this card</Text>
22
+ * </Card>
23
+ *
24
+ * @example
25
+ * // Presentation Card with interactive children
26
+ * <Card role="presentation">
27
+ * <Text>This card is not interactive</Text>
28
+ * <Link href="/details">But this link is</Link>
18
29
  * </Card>
19
30
  */
20
31
 
@@ -32,6 +43,7 @@ const Card = ({
32
43
  const containerRef = useRef<HTMLDivElement>(null);
33
44
  const linkRef = useRef<HTMLAnchorElement>(null);
34
45
  const isRoleLink = role === "link";
46
+ const isRolePresentation = role === "presentation";
35
47
  const checkedConditions = role === "checkbox" ? selected : undefined;
36
48
 
37
49
  const cardContext: TypeCardContext = {
@@ -48,10 +60,10 @@ const Card = ({
48
60
 
49
61
  return (
50
62
  <StyledCard
51
- tabIndex={isRoleLink ? -1 : 0}
63
+ tabIndex={isRoleLink || isRolePresentation ? -1 : 0}
52
64
  role={isRoleLink ? undefined : role}
53
- onClick={handleClickConditions}
54
- onKeyDown={handleKeyDown}
65
+ onClick={isRolePresentation ? undefined : handleClickConditions}
66
+ onKeyDown={isRolePresentation ? undefined : handleKeyDown}
55
67
  $elevation={elevation}
56
68
  ref={containerRef}
57
69
  $selected={selected}
package/src/CardTypes.ts CHANGED
@@ -91,12 +91,12 @@ export type TypePresentationCardProps = {
91
91
  role: "presentation";
92
92
  href?: never;
93
93
  /**
94
- * **Warning:**
95
- * `role='presentation'` is outside of the accessibility tree.
96
- * Using an `onClick` that performs a user action should likely be used
97
- * with `role='button'` instead.
94
+ * **Not supported for accessibility:**
95
+ * `role='presentation'` removes the element from the accessibility tree.
96
+ * Presentation Cards do not support `onClick` handlers or keyboard interactions.
97
+ * Use `role='button'` for interactive Cards instead.
98
98
  */
99
- onClick?: TypeOnClick;
99
+ onClick?: never;
100
100
  selected?: never;
101
101
  };
102
102
 
@@ -91,14 +91,14 @@ describe("A card is interactive", () => {
91
91
  expect(card).toHaveAttribute("aria-disabled", "true");
92
92
  });
93
93
 
94
- it("should have an adjustable hover state style", async () => {
94
+ it("should have an adjustable hover state style for interactive cards", async () => {
95
95
  const { user } = render(
96
- <Card role="presentation" elevation="high">
96
+ <Card role="button" onClick={mockCardClick} elevation="high">
97
97
  Test
98
98
  </Card>
99
99
  );
100
100
 
101
- const card = screen.getByRole("presentation");
101
+ const card = screen.getByRole("button");
102
102
 
103
103
  // apparently, jest-dom can't do this with toHaveStyle
104
104
  // https://github.com/testing-library/jest-dom/issues/59
@@ -107,20 +107,19 @@ describe("A card is interactive", () => {
107
107
  // https://github.com/styled-components/jest-styled-components#tohavestylerule
108
108
  await user.hover(card);
109
109
  expect(card).toHaveStyleRule("box-shadow", theme.shadows.high, {
110
- modifier: ":hover",
110
+ modifier: '&[role="button"]:hover',
111
111
  });
112
112
  });
113
113
 
114
- it("can be focused", async () => {
114
+ it("can be focused when interactive", async () => {
115
115
  const { user } = render(
116
- <Card role="presentation">
116
+ <Card role="button" onClick={mockCardClick}>
117
117
  Test
118
118
  <button>child click test</button>
119
119
  </Card>
120
120
  );
121
121
 
122
- const card = screen.getByRole("presentation");
123
- const button = screen.getByRole("button");
122
+ const [card, button] = screen.getAllByRole("button");
124
123
 
125
124
  expect(card).toHaveAttribute("tabindex", "0");
126
125
 
@@ -190,18 +189,19 @@ describe("A card is interactive", () => {
190
189
  );
191
190
  });
192
191
 
193
- it("should support interactive children", async () => {
192
+ it("should support interactive children in button cards", async () => {
194
193
  const mockChildClick = jest.fn((e) => e.stopPropagation());
195
194
 
196
195
  const { user } = render(
197
- <Card role="presentation" onClick={mockCardClick}>
196
+ <Card role="button" onClick={mockCardClick}>
198
197
  Test
199
198
  <button onClick={mockChildClick}>child click test</button>
200
199
  </Card>
201
200
  );
202
201
 
203
- const card = screen.getByRole("presentation");
204
- const cardChild = screen.getByText("child click test");
202
+ const buttons = screen.getAllByRole("button");
203
+ const card = buttons[0]!;
204
+ const cardChild = buttons[1]!;
205
205
 
206
206
  // Expect clicking the card to trigger the card's onClick but not the child's
207
207
  await user.click(card);
@@ -213,6 +213,27 @@ describe("A card is interactive", () => {
213
213
  expect(mockCardClick).toBeCalledTimes(1);
214
214
  expect(mockChildClick).toBeCalledTimes(1);
215
215
  });
216
+
217
+ it("presentation cards are not interactive but support interactive children", async () => {
218
+ const mockChildClick = jest.fn();
219
+
220
+ const { user } = render(
221
+ <Card role="presentation">
222
+ Test
223
+ <button onClick={mockChildClick}>child click test</button>
224
+ </Card>
225
+ );
226
+
227
+ const card = screen.getByRole("presentation");
228
+ const cardChild = screen.getByText("child click test");
229
+
230
+ // Presentation cards should not be focusable
231
+ expect(card).toHaveAttribute("tabindex", "-1");
232
+
233
+ // Interactive children should still work
234
+ await user.click(cardChild);
235
+ expect(mockChildClick).toBeCalledTimes(1);
236
+ });
216
237
  });
217
238
 
218
239
  describe("A Card supports composable layouts", () => {
@@ -52,6 +52,7 @@ function CardTypes() {
52
52
  This is a card.
53
53
  </Card>
54
54
  <Card role="presentation">This is a card.</Card>
55
+ {/* @ts-expect-error - test that onClick is never allowed with role=presentation */}
55
56
  <Card
56
57
  role="presentation"
57
58
  onClick={() => {
package/src/styles.tsx CHANGED
@@ -117,17 +117,22 @@ export const StyledCard = styled.div<TypeStyledCard>`
117
117
  &[role="button"],
118
118
  &[role="checkbox"] {
119
119
  cursor: pointer;
120
+
121
+ &:hover {
122
+ box-shadow: ${({ theme, $elevation = "low" }) =>
123
+ theme.shadows[$elevation]};
124
+ }
120
125
  }
121
126
 
122
- ${({ $isRoleLink }) =>
127
+ ${({ $isRoleLink, theme, $elevation = "low" }) =>
123
128
  $isRoleLink &&
124
129
  `
125
130
  cursor: pointer;
126
- `}
127
131
 
128
- &:hover {
129
- box-shadow: ${({ theme, $elevation = "low" }) => theme.shadows[$elevation]};
130
- }
132
+ &:hover {
133
+ box-shadow: ${theme.shadows[$elevation]};
134
+ }
135
+ `}
131
136
 
132
137
  &:focus-within {
133
138
  ${({ $isRoleLink }) => ($isRoleLink ? focusRing : null)}