Skip to content

Commit af927d8

Browse files
Merge pull request #130 from buildo/fix_localized_link
Fix handling LocalizedLink
2 parents a733cfd + 5acce74 commit af927d8

File tree

10 files changed

+107
-31
lines changed

10 files changed

+107
-31
lines changed

Diff for: packages/bento-design-system/src/BentoProvider.tsx

+12-4
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import { Children } from ".";
22
import { ToastProviderProps } from "./Toast/createToastProvider";
33
import { OverlayProvider } from "@react-aria/overlays";
44
import { DefaultMessages, DefaultMessagesContext } from "./DefaultMessagesContext";
5-
import { LinkComponent, LinkComponentContext } from "./util/link";
6-
import { FunctionComponent, useContext } from "react";
5+
import { LinkComponentContext, LinkComponentProps } from "./util/link";
6+
import { ComponentType, FunctionComponent, useContext } from "react";
7+
import { LinkConfig } from "./Link/Config";
78

89
type Props = {
910
children?: Children;
@@ -29,7 +30,8 @@ type Props = {
2930
*
3031
* Defaults to a regular `<a>` tag.
3132
*/
32-
linkComponent?: LinkComponent;
33+
linkComponent?: ComponentType<LinkComponentProps>;
34+
linkConfig?: LinkConfig;
3335
} & DefaultMessages;
3436

3537
export function createBentoProvider(ToastProvider: FunctionComponent<ToastProviderProps>) {
@@ -38,13 +40,19 @@ export function createBentoProvider(ToastProvider: FunctionComponent<ToastProvid
3840
toastDismissAfterMs = 5000,
3941
defaultMessages,
4042
linkComponent,
43+
linkConfig,
4144
}: Props) {
4245
const linkComponentFromContext = useContext(LinkComponentContext);
4346

4447
return (
4548
<OverlayProvider>
4649
<DefaultMessagesContext.Provider value={{ defaultMessages }}>
47-
<LinkComponentContext.Provider value={linkComponent || linkComponentFromContext}>
50+
<LinkComponentContext.Provider
51+
value={{
52+
component: linkComponent || linkComponentFromContext.component,
53+
config: linkConfig || linkComponentFromContext.config,
54+
}}
55+
>
4856
<ToastProvider dismissAfterMs={toastDismissAfterMs}>{children}</ToastProvider>
4957
</LinkComponentContext.Provider>
5058
</DefaultMessagesContext.Provider>

Diff for: packages/bento-design-system/src/Button/ButtonLink.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export function createButtonLink(config: ButtonConfig) {
4747
<Box
4848
{...linkProps}
4949
{...props}
50-
as={LinkComponent}
50+
as={LinkComponent.component}
5151
href={href}
5252
target={target}
5353
className={[buttonRecipe({ kind, hierarchy, size, active }), element.a]}

Diff for: packages/bento-design-system/src/Link/createLink.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export function createLink(config: LinkConfig) {
3434
<Box
3535
{...linkProps}
3636
{...props}
37-
as={LinkComponent}
37+
as={LinkComponent.component}
3838
href={href}
3939
className={[link, extendedHitAreaRecipe({ axis: "y" }), resetStyles.element["a"]]}
4040
disabled={isDisabled}

Diff for: packages/bento-design-system/src/List/createListItem.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ export function createListItem(config: ListItemConfig) {
9292
>
9393
<Box
9494
ref={linkRef}
95-
as={props.href ? LinkComponent : "div"}
95+
as={props.href ? LinkComponent.component : "div"}
9696
className={element.a}
9797
{...linkProps}
9898
href={props.href}

Diff for: packages/bento-design-system/src/Navigation/createNavigation.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export function createNavigation(config: NavigationConfig) {
7979
<Box
8080
tabIndex={active || disabled ? -1 : 0}
8181
className={[destinationRecipe({ active }), element.a]}
82-
as={LinkComponent}
82+
as={LinkComponent.component}
8383
{...linkProps}
8484
href={href}
8585
display="block"

Diff for: packages/bento-design-system/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export * from "./Typography/Title/Title";
5555
export * from "./sprinkles";
5656
export * from "./util/Children";
5757
export * from "./util/LocalizedString";
58+
export * from "./util/NonEmptyArray";
5859
export * from "./util/Omit";
5960
export * from "./util/TextChildren";
6061
export * from "./util/align";

Diff for: packages/bento-design-system/src/util/TextChildren.tsx

+33-17
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
import { HTMLAttributeAnchorTarget, isValidElement } from "react";
1+
import clsx from "clsx";
2+
import { ComponentType, HTMLAttributeAnchorTarget, isValidElement } from "react";
23
import flattenChildren from "react-keyed-flatten-children";
4+
import { ButtonProps } from "../Button/createButton";
35
import { Box } from "../internal/Box/Box";
46
import { Children } from "./Children";
5-
import { LinkComponent, useLinkComponent } from "./link";
7+
import { LinkComponentProps, useLinkComponent } from "./link";
68
import { LocalizedString } from "./LocalizedString";
79
import { NonEmptyArray } from "./NonEmptyArray";
810
import { splitBy } from "./splitBy";
11+
import { link as linkStyle } from "../Link/Link.css";
12+
import { LinkConfig } from "../Link/Config";
13+
import { bentoSprinkles } from "../internal";
914

1015
/** `TextChildren` is a DSL for building type-safe rich localized strings.
1116
* It's the composition of strings that have been localized (`LocalizedString`) and other elements
@@ -51,31 +56,37 @@ export function bold(text: LocalizedString): LocalizedBold {
5156
return { type: "bold", text };
5257
}
5358

54-
export function link(
55-
text: LocalizedString,
56-
href: string,
57-
target?: HTMLAttributeAnchorTarget
58-
): LocalizedLink {
59-
return { type: "link", text, href, target };
59+
export type LinkOptions =
60+
| {
61+
href: string;
62+
target?: HTMLAttributeAnchorTarget;
63+
}
64+
| {
65+
onClick: ButtonProps["onPress"];
66+
};
67+
68+
export function link(text: LocalizedString, options: LinkOptions): LocalizedLink {
69+
return { type: "link", text, ...options };
6070
}
6171

6272
export type LocalizedLink = {
6373
type: "link";
6474
text: LocalizedString;
65-
href: string;
66-
target?: HTMLAttributeAnchorTarget;
67-
};
75+
} & LinkOptions;
6876

6977
export function makeTextChildrenFromElements(c: TextChildrenConcreteType) {
7078
return c as TextChildren;
7179
}
7280

73-
function textChildrenToChildrenArray(
81+
export function textChildrenToChildrenArray(
7482
children: TextChildren,
75-
LinkComponent: LinkComponent
83+
LinkComponent: ComponentType<LinkComponentProps>,
84+
linkConfig: LinkConfig
7685
): Array<Children> {
7786
if (Array.isArray(children)) {
78-
return children.flatMap((c) => textChildrenToChildrenArray(c as TextChildren, LinkComponent));
87+
return children.flatMap((c) =>
88+
textChildrenToChildrenArray(c as TextChildren, LinkComponent, linkConfig)
89+
);
7990
} else if (typeof children === "string") {
8091
return [children];
8192
}
@@ -92,19 +103,24 @@ function textChildrenToChildrenArray(
92103
return [<br />];
93104
case "link":
94105
return [
95-
<Box as={LinkComponent} href={children.href} target={children.target}>
106+
<Box
107+
as={LinkComponent}
108+
className={clsx(linkStyle, bentoSprinkles({ fontWeight: "label" }))}
109+
textDecoration={linkConfig.labelDecoration}
110+
{...children}
111+
>
96112
{children.text}
97113
</Box>,
98114
];
99115
}
100116
}
101117

102118
export function useTextChildrenToChildren() {
103-
const LinkComponent = useLinkComponent();
119+
const { component: LinkComponent, config: linkConfig } = useLinkComponent();
104120

105121
return function textChildrenToChildren(children: TextChildren): Children {
106122
const lines = splitBy(
107-
textChildrenToChildrenArray(children, LinkComponent),
123+
textChildrenToChildrenArray(children, LinkComponent, linkConfig),
108124
(e) => isValidElement(e) && e.type === "br"
109125
);
110126

Diff for: packages/bento-design-system/src/util/link.tsx

+7-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import {
66
ForwardRefRenderFunction,
77
useContext,
88
} from "react";
9+
import { defaultConfigs } from "..";
910
import { Box } from "../internal";
11+
import { LinkConfig } from "../Link/Config";
1012

1113
export type LinkComponentProps = {
1214
href: string;
@@ -16,11 +18,12 @@ export const makeLinkComponent = (
1618
render: ForwardRefRenderFunction<HTMLAnchorElement, LinkComponentProps>
1719
) => forwardRef(render);
1820

19-
export type LinkComponent = ComponentType<LinkComponentProps>;
21+
export type LinkComponent = { component: ComponentType<LinkComponentProps>; config: LinkConfig };
2022

21-
export const DefaultLinkComponent = makeLinkComponent((props, ref) => (
22-
<Box as="a" ref={ref} {...props} />
23-
));
23+
export const DefaultLinkComponent = {
24+
component: makeLinkComponent((props, ref) => <Box as="a" ref={ref} {...props} />),
25+
config: defaultConfigs.link,
26+
};
2427

2528
export const LinkComponentContext = createContext<LinkComponent>(DefaultLinkComponent);
2629

Diff for: packages/bento-design-system/test/useTextChildrenToChildren.test.tsx

+26-2
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ describe("textChildrenToChildren", () => {
5757

5858
test("link", () => {
5959
const input = makeTextChildrenFromElements(
60-
link(formatMessage("Link"), "https://www.google.com", "_blank")
60+
link(formatMessage("Link"), { href: "https://www.google.com", target: "_blank" })
6161
);
6262
const result = toChildren(input);
6363
expect(result).toMatchInlineSnapshot(`
@@ -69,8 +69,20 @@ describe("textChildrenToChildren", () => {
6969
"render": [Function],
7070
}
7171
}
72+
className="Link__fmzr140 sprinkles_cursor_pointer_default__oc4xkuvq sprinkles_cursor_notAllowed_disabled__oc4xkuw4 sprinkles_outline_none_default__oc4xkuys sprinkles_color_linkEnabled_default__oc4xkuk7 sprinkles_color_linkHover_hover__oc4xkukd sprinkles_color_linkFocus_focus__oc4xkukj sprinkles_color_linkDisabled_disabled__oc4xkukq sprinkles_fontWeight_label__oc4xkuy"
7273
href="https://www.google.com"
7374
target="_blank"
75+
text="Link"
76+
textDecoration={
77+
Object {
78+
"active": "none",
79+
"default": "none",
80+
"disabled": "none",
81+
"focus": "underline",
82+
"hover": "underline",
83+
}
84+
}
85+
type="link"
7486
>
7587
Link
7688
</ForwardRef>,
@@ -117,7 +129,7 @@ describe("textChildrenToChildren", () => {
117129
lineBreak,
118130
bold(formatMessage("Second")),
119131
formatMessage("line"),
120-
link(formatMessage("Link"), "https://www.google.com", "_blank"),
132+
link(formatMessage("Link"), { href: "https://www.google.com", target: "_blank" }),
121133
]);
122134
const result = toChildren(input);
123135
expect(result).toMatchInlineSnapshot(`
@@ -144,8 +156,20 @@ describe("textChildrenToChildren", () => {
144156
"render": [Function],
145157
}
146158
}
159+
className="Link__fmzr140 sprinkles_cursor_pointer_default__oc4xkuvq sprinkles_cursor_notAllowed_disabled__oc4xkuw4 sprinkles_outline_none_default__oc4xkuys sprinkles_color_linkEnabled_default__oc4xkuk7 sprinkles_color_linkHover_hover__oc4xkukd sprinkles_color_linkFocus_focus__oc4xkukj sprinkles_color_linkDisabled_disabled__oc4xkukq sprinkles_fontWeight_label__oc4xkuy"
147160
href="https://www.google.com"
148161
target="_blank"
162+
text="Link"
163+
textDecoration={
164+
Object {
165+
"active": "none",
166+
"default": "none",
167+
"disabled": "none",
168+
"focus": "underline",
169+
"hover": "underline",
170+
}
171+
}
172+
type="link"
149173
>
150174
Link
151175
</ForwardRef>,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { action } from "@storybook/addon-actions";
2+
import { Body, link, makeTextChildrenFromElements } from "..";
3+
import { createComponentStories, formatMessage } from "../util";
4+
5+
const { defaultExport, createStory } = createComponentStories({
6+
component: Body,
7+
args: {},
8+
parameters: {
9+
// NOTE(gabro): this is to avoid Storybook erroring when trying to parse the DSL
10+
// See https://github.com/storybookjs/storybook/issues/11543#issuecomment-684130442
11+
docs: { source: { type: "code" } },
12+
},
13+
});
14+
15+
export default defaultExport;
16+
17+
export const LinkButton = createStory({
18+
size: "medium",
19+
children: makeTextChildrenFromElements(
20+
link(formatMessage("I look like a link, but I'm actually a button!"), {
21+
onClick: action("onClick"),
22+
})
23+
),
24+
});

0 commit comments

Comments
 (0)