@sproutsocial/seeds-react-card 1.1.13 → 1.1.15
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.
- package/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +13 -0
- package/dist/esm/index.js +13 -8
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +17 -6
- package/dist/index.d.ts +17 -6
- package/dist/index.js +13 -8
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
- package/src/Card.tsx +16 -4
- package/src/CardTypes.ts +5 -5
- package/src/__tests__/Card.test.tsx +33 -12
- package/src/__tests__/CardTypes.typetest.tsx +1 -0
- package/src/styles.tsx +10 -5
package/.turbo/turbo-build.log
CHANGED
|
@@ -8,14 +8,14 @@ $ tsup --dts
|
|
|
8
8
|
[34mCLI[39m Cleaning output folder
|
|
9
9
|
[34mCJS[39m Build start
|
|
10
10
|
[34mESM[39m Build start
|
|
11
|
-
[32mCJS[39m [1mdist/index.js [22m[
|
|
12
|
-
[32mCJS[39m [1mdist/index.js.map [22m[32m21.
|
|
13
|
-
[32mCJS[39m ⚡️ Build success in
|
|
14
|
-
[32mESM[39m [1mdist/esm/index.js [22m[
|
|
15
|
-
[32mESM[39m [1mdist/esm/index.js.map [22m[32m21.
|
|
16
|
-
[32mESM[39m ⚡️ Build success in
|
|
11
|
+
[32mCJS[39m [1mdist/index.js [22m[32m11.10 KB[39m
|
|
12
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m21.91 KB[39m
|
|
13
|
+
[32mCJS[39m ⚡️ Build success in 138ms
|
|
14
|
+
[32mESM[39m [1mdist/esm/index.js [22m[32m8.06 KB[39m
|
|
15
|
+
[32mESM[39m [1mdist/esm/index.js.map [22m[32m21.89 KB[39m
|
|
16
|
+
[32mESM[39m ⚡️ Build success in 142ms
|
|
17
17
|
[34mDTS[39m Build start
|
|
18
|
-
[32mDTS[39m ⚡️ Build success in
|
|
19
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m5.
|
|
20
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[32m5.
|
|
21
|
-
Done in
|
|
18
|
+
[32mDTS[39m ⚡️ Build success in 16856ms
|
|
19
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m5.60 KB[39m
|
|
20
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m5.60 KB[39m
|
|
21
|
+
Done in 22.43s.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @sproutsocial/seeds-react-card
|
|
2
2
|
|
|
3
|
+
## 1.1.15
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @sproutsocial/seeds-react-icon@2.0.5
|
|
8
|
+
- @sproutsocial/seeds-react-mixins@4.2.1
|
|
9
|
+
|
|
10
|
+
## 1.1.14
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 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.
|
|
15
|
+
|
|
3
16
|
## 1.1.13
|
|
4
17
|
|
|
5
18
|
### 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
|
-
|
|
110
|
-
|
|
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,
|
package/dist/esm/index.js.map
CHANGED
|
@@ -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
|
-
* **
|
|
63
|
-
* `role='presentation'`
|
|
64
|
-
*
|
|
65
|
-
*
|
|
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?:
|
|
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
|
-
*
|
|
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
|
-
* **
|
|
63
|
-
* `role='presentation'`
|
|
64
|
-
*
|
|
65
|
-
*
|
|
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?:
|
|
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
|
-
*
|
|
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
|
-
|
|
142
|
-
|
|
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,gBAAwC;;;ACAxC,+BAAmB;AACnB,2BASO;AACP,gCAAoC;AAOpC,8BAAiB;AAOV,IAAM,oBAAoB,yBAAAC,QAAO;AAAA;AAAA;AAAA,aAG3B,CAAC,EAAE,MAAM,MAAM,MAAM,MAAM,GAAG,CAAC;AAAA;AAAA;AAAA,IAGxC,2BAAM;AAAA,IACN,0BAAK;AAAA,IACL,4BAAO;AAAA,IACP,yBAAI;AAAA,IACJ,2BAAM;AAAA,IACN,0BAAK;AAAA;AAGF,IAAM,uBAAmB,yBAAAA,SAAO,iBAAiB;AAAA;AAAA,mBAErC,CAAC,EAAE,MAAM,MAAM,GAAG,MAAM,aAAa,GAAG,CAAC;AAAA,IACxD,MAAM,OAAO,UAAU,OAAO,IAAI,EAAE;AAAA,4BACZ,CAAC,EAAE,MAAM,MAAM,MAAM,MAAM,KAAK;AAAA,6BAC/B,CAAC,EAAE,MAAM,MAAM,MAAM,MAAM,KAAK;AAAA;AAAA,IAEzD,2BAAM;AAAA,IACN,0BAAK;AAAA,IACL,4BAAO;AAAA,IACP,yBAAI;AAAA,IACJ,2BAAM;AAAA,IACN,0BAAK;AAAA;AAGF,IAAM,uBAAmB,yBAAAA,SAAO,iBAAiB;AAAA;AAAA,gBAExC,CAAC,EAAE,MAAM,MAAM,GAAG,MAAM,aAAa,GAAG,CAAC;AAAA,IACrD,MAAM,OAAO,UAAU,OAAO,IAAI,EAAE;AAAA,+BACT,CAAC,EAAE,MAAM,MAAM,MAAM,MAAM,KAAK;AAAA,gCAC/B,CAAC,EAAE,MAAM,MAAM,MAAM,MAAM,KAAK;AAAA;AAAA,IAE5D,2BAAM;AAAA,IACN,0BAAK;AAAA,IACL,4BAAO;AAAA,IACP,yBAAI;AAAA,IACJ,2BAAM;AAAA,IACN,0BAAK;AAAA;AAGF,IAAM,sBAAsB,yBAAAA,QAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASnC,IAAM,yBAAqB,yBAAAA,SAAO,wBAAAC,OAAI;AAAA;AAAA,gBAE7B,CAAC,EAAE,MAAM,MAAM,MAAM,OAAO,UAAU,WAAW,IAAI;AAAA;AAAA,wBAE7C,CAAC,EAAE,MAAM,MAAM,MAAM,SAAS,MAAM;AAAA;AAAA,IAExD,CAAC,EAAE,UAAU,MACb,aACA;AAAA;AAAA,GAED;AAAA;AAGI,IAAM,iBAAiB,yBAAAD,QAAO;AAAA,iBACpB,CAAC,MAAM,EAAE,MAAM,UAAU;AAAA,iBACzB,CAAC,MAAM,EAAE,MAAM,YAAY,IAAI;AAAA,WACrC,CAAC,MAAM,EAAE,MAAM,OAAO,KAAK,QAAQ;AAAA,IAC1C,CAAC,MAAM,EAAE,MAAM,WAAW,GAAG,CAAC;AAAA;AAAA,IAE9B,0BAAK;AAAA,IACL,+BAAU;AAAA;AAGP,IAAM,aAAa,yBAAAA,QAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gBAMjB,CAAC,EAAE,MAAM,MAAM,MAAM,OAAO,UAAU,WAAW,IAAI;AAAA,YACzD,CAAC,EAAE,MAAM,MAAM,MAAM,aAAa,GAAG,CAAC;AAAA,MAC5C,CAAC,EAAE,MAAM,MAAM,MAAM,OAAO,UAAU,OAAO,IAAI;AAAA,aAC1C,CAAC,EAAE,OAAO,yBAAyB,MAC5C,2BAA2B,IAAI,MAAM,MAAM,GAAG,CAAC;AAAA,mBAChC,CAAC,EAAE,MAAM,MAAM,MAAM,MAAM,KAAK;AAAA,2BACxB,CAAC,EAAE,MAAM,MAAM,MAAM,SAAS,MAAM;AAAA,aAClD,CAAC,EAAE,MAAM,MAAM,MAAM,SAAS,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAO7C,CAAC,EAAE,YAAY,MACf,eACA;AAAA;AAAA,EAEF;AAAA;AAAA;AAAA,kBAGgB,CAAC,EAAE,OAAO,aAAa,MAAM,MAAM,MAAM,QAAQ,UAAU,CAAC;AAAA;AAAA;AAAA;AAAA,MAIxE,CAAC,EAAE,YAAY,MAAO,cAAc,sCAAY,IAAK;AAAA,MACrD,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAQd,mCAAS;AAAA;AAAA;AAAA,IAGX,CAAC,EAAE,UAAU,MACb,aACA;AAAA,MACE,kCAAQ;AAAA,GACX;AAAA;AAAA,IAEC,CAAC,EAAE,WAAW,MAAM,MACpB,aACA;AAAA,cACU,MAAM,aAAa,GAAG,CAAC,UAAU,MAAM,OAAO,UAAU,OAAO,QAAQ;AAAA,GAClF;AAAA;AAAA,IAEC,2BAAM;AAAA,IACN,0BAAK;AAAA,IACL,4BAAO;AAAA,IACP,yBAAI;AAAA,IACJ,2BAAM;AAAA,IACN,6BAAQ;AAAA,IACR,0BAAK;AAAA;AAGF,IAAM,2BAAuB,yBAAAA,SAAO,wBAAAC,OAAI;AAAA,IAC3C,UAAU;AAAA,4BACc,CAAC,MAAM,EAAE,MAAM,MAAM,GAAG,CAAC;AAAA;AAAA,gBAErC,CAAC,MAAM,EAAE,MAAM,SAAS,MAAM;AAAA;;;ACzK9C,mBAAqD;AACrD,mCAAgC;AAGzB,IAAM,0BAAsB,4BAA+B;AAAA;AAAA,EAEhE,oBAAoB,MAAM;AAAA,EAAC;AAAA,EAC3B,MAAM;AAAA,EACN,SAAS;AACX,CAAC;AAEM,SAAS,kBAAkB;AAChC,QAAM,EAAE,mBAAmB,QAAI,yBAAW,mBAAmB;AAC7D,8BAAU,MAAM;AACd,0BAAsB,mBAAmB,IAAI;AAAA,EAC/C,GAAG,CAAC,kBAAkB,CAAC;AACzB;AAOO,IAAM,aAAa,CAAC,EAAE,GAAG,MAAM,IAAI,MAAwB;AAChE,QAAM,EAAE,OAAO,IAAI;AAGnB,oDAAgB,MAAM;AAEtB,MAAI,IAAI,SAAS,SAAS,MAAM,GAAG;AACjC,QACE,OAAO,aAAa,SAAS,MAAM,QACnC,OAAO,aAAa,MAAM,MAAM,MAChC;AACA,QAAE,gBAAgB;AAClB;AAAA,IACF;AAAA,EACF;AAEA,SAAO,KAAK,MAAM,QAAQ,GAAG,MAAM;AACrC;AAQO,IAAM,YAAY,CAAC,EAAE,GAAG,MAAM,SAAS,KAAK,KAAK,MAAuB;AAC7E,MAAI,GAAG,QAAQ,SAAS;AACtB,QAAI,SAAS,QAAQ;AACnB,aAAO,WAAW,EAAE,GAAG,MAAM,IAAI,CAAC;AAAA,IACpC;AAEA,QAAI,SAAS,gBAAgB;AAC3B;AAAA,IACF;AAEA,WAAO,UAAU,CAAC;AAAA,EACpB;AACF;;;AC5DA,IAAAC,gBAAkC;AA4BzB;AAPF,IAAM,cAAc,CAAC;AAAA,EAC1B;AAAA,EACA,GAAG;AACL,MAAmC;AAGjC,kBAAgB;AAChB,SAAO,4CAAC,qBAAmB,GAAG,MAAO,UAAS;AAChD;AAEO,IAAM,aAAa,CAAC;AAAA,EACzB;AAAA,EACA,GAAG;AACL,MAAmC;AACjC,kBAAgB;AAChB,SAAO,4CAAC,oBAAkB,GAAG,MAAO,UAAS;AAC/C;AAEO,IAAM,aAAa,CAAC;AAAA,EACzB;AAAA,EACA,GAAG;AACL,MAAmC;AACjC,kBAAgB;AAChB,SAAO,4CAAC,oBAAkB,GAAG,MAAO,UAAS;AAC/C;AAMO,IAAM,eAAe,CAAC,EAAE,UAAU,MAA6B;AACpE,SACE,4CAAC,uBACC;AAAA,IAAC;AAAA;AAAA,MACC,eAAW;AAAA,MACX,OAAM;AAAA,MACN,MAAK;AAAA,MACL;AAAA;AAAA,EACF,GACF;AAEJ;AAEO,IAAM,iBAAiB,CAAC,EAAE,GAAG,KAAK,MAAM;AAC7C,SACE;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ,MAAK;AAAA,MACL,MAAK;AAAA,MAEL,eAAW;AAAA;AAAA,EACb;AAEJ;AAEO,IAAM,WAAW,CAAC;AAAA,EACvB;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,OAAAC;AAAA,EACA,GAAG;AACL,MAA6C;AAC3C,QAAM,EAAE,MAAM,QAAQ,QAAI,0BAAW,mBAAmB;AAGxD,QAAM,cAAc,CAAC,MAA2C;AAC9D,MAAE,gBAAgB;AAAA,EACpB;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ,QAAQ,WAAW,WAAW;AAAA,MAC9B,KAAK,WAAW,eAAe;AAAA,MAC/B;AAAA,MACA,SAAS;AAAA,MACT,KAAK;AAAA,MAIL,OAAOA;AAAA,MAEP,sFACG;AAAA;AAAA,QACA,aAAa,4CAAC,kBAAe,IAAI,KAAK,IAAK;AAAA,SAC9C;AAAA;AAAA,EACF;AAEJ;;;AH5DI,IAAAC,sBAAA;AA7BJ,IAAM,OAAO,CAAC;AAAA,EACZ;AAAA,EACA,UAAAC,YAAW;AAAA,EACX,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA,GAAG;AACL,MAAqB;AACnB,QAAM,CAAC,iBAAiB,kBAAkB,QAAI,wBAAkB,KAAK;AACrE,QAAM,mBAAe,sBAAuB,IAAI;AAChD,QAAM,cAAU,sBAA0B,IAAI;AAC9C,QAAM,aAAa,SAAS;AAC5B,QAAM,oBAAoB,SAAS,aAAa,WAAW;AAE3D,QAAM,cAA+B;AAAA,IACnC;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,QAAM,wBAAiD,CAAC,MACtD,aAAa,QAAQ,SAAS,MAAM,IAAI,UAAU,CAAC;AAErD,QAAM,gBAA4D,CAAC,MACjE,UAAU,EAAE,GAAG,MAAM,SAAS,KAAK,cAAc,KAAK,CAAC;AAEzD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU,aAAa,KAAK;AAAA,MAC5B,MAAM,aAAa,SAAY;AAAA,MAC/B,SAAS;AAAA,MACT,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,KAAK;AAAA,MACL,WAAW;AAAA,MACX,gBAAc;AAAA,MACd,WAAWA;AAAA,MACX,iBAAeA,aAAYA;AAAA,MAC3B,0BAA0B;AAAA,MAC1B,aAAa;AAAA,MACZ,GAAG;AAAA,MAEJ;AAAA,qDAAC,gBAAa,WAAW,UAAU;AAAA,QACnC,6CAAC,oBAAoB,UAApB,EAA6B,OAAO,aAClC,UACH;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,eAAQ;;;AIvEf,IAAAC,SAAuB;;;ALCvB,IAAO,gBAAQ;","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.
|
|
3
|
+
"version": "1.1.15",
|
|
4
4
|
"description": "Seeds React Card",
|
|
5
5
|
"author": "Sprout Social, Inc.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@sproutsocial/seeds-react-system-props": "^3.0.1",
|
|
22
|
-
"@sproutsocial/seeds-react-mixins": "^4.2.
|
|
22
|
+
"@sproutsocial/seeds-react-mixins": "^4.2.1",
|
|
23
23
|
"@sproutsocial/seeds-react-utilities": "^4.0.1",
|
|
24
|
-
"@sproutsocial/seeds-react-icon": "^2.0.
|
|
24
|
+
"@sproutsocial/seeds-react-icon": "^2.0.5",
|
|
25
25
|
"styled-system": "^5.1.5"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
@@ -34,14 +34,14 @@
|
|
|
34
34
|
"@sproutsocial/seeds-tsconfig": "*",
|
|
35
35
|
"@sproutsocial/seeds-testing": "*",
|
|
36
36
|
"@sproutsocial/seeds-react-testing-library": "*",
|
|
37
|
-
"@sproutsocial/seeds-react-banner": "^1.0.
|
|
38
|
-
"@sproutsocial/seeds-react-badge": "^2.0.
|
|
39
|
-
"@sproutsocial/seeds-react-box": "^1.1.
|
|
40
|
-
"@sproutsocial/seeds-react-link": "^1.0.
|
|
41
|
-
"@sproutsocial/seeds-react-partner-logo": "^1.5.
|
|
37
|
+
"@sproutsocial/seeds-react-banner": "^1.0.16",
|
|
38
|
+
"@sproutsocial/seeds-react-badge": "^2.0.8",
|
|
39
|
+
"@sproutsocial/seeds-react-box": "^1.1.9",
|
|
40
|
+
"@sproutsocial/seeds-react-link": "^1.0.8",
|
|
41
|
+
"@sproutsocial/seeds-react-partner-logo": "^1.5.1",
|
|
42
42
|
"@sproutsocial/seeds-react-spot-illustration": "^1.1.1",
|
|
43
43
|
"@sproutsocial/seeds-react-text": "^1.3.2",
|
|
44
|
-
"@sproutsocial/seeds-react-theme": "^3.3.
|
|
44
|
+
"@sproutsocial/seeds-react-theme": "^3.3.1"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"styled-components": "^5.2.3"
|
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
|
-
*
|
|
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
|
-
* **
|
|
95
|
-
* `role='presentation'`
|
|
96
|
-
*
|
|
97
|
-
*
|
|
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?:
|
|
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="
|
|
96
|
+
<Card role="button" onClick={mockCardClick} elevation="high">
|
|
97
97
|
Test
|
|
98
98
|
</Card>
|
|
99
99
|
);
|
|
100
100
|
|
|
101
|
-
const card = screen.getByRole("
|
|
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="
|
|
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.
|
|
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="
|
|
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
|
|
204
|
-
const
|
|
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", () => {
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
132
|
+
&:hover {
|
|
133
|
+
box-shadow: ${theme.shadows[$elevation]};
|
|
134
|
+
}
|
|
135
|
+
`}
|
|
131
136
|
|
|
132
137
|
&:focus-within {
|
|
133
138
|
${({ $isRoleLink }) => ($isRoleLink ? focusRing : null)}
|