Skip to content

Commit c0f4f39

Browse files
fix: make Sandbox generic to resolve typescript vs data attributes
1 parent 9d2383a commit c0f4f39

19 files changed

+77
-155
lines changed

src/components/sandbox/Sandbox.tsx

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ type Flag = "reactive" | "template-driven" | "event";
2121
type ComponentType = "goa" | "codesnippet";
2222
type Serializer = (el: any, properties: ComponentBinding[]) => string;
2323

24-
interface SandboxProps {
24+
interface SandboxProps<T = Record<string, unknown>> {
2525
properties?: ComponentBinding[];
2626
formItemProperties?: ComponentBinding[];
2727
note?: string | { type?: GoabCalloutType; heading?: string; content: string };
2828
fullWidth?: boolean;
29-
onChange?: (bindings: ComponentBinding[], props: Record<string, unknown>) => void;
29+
onChange?: (bindings: ComponentBinding[], props: T) => void;
3030
onChangeFormItemBindings?: (bindings: ComponentBinding[], props: Record<string, unknown>) => void;
3131
flags?: Flag[];
3232
skipRender?: boolean; // prevent rendering the snippet, to allow custom code to be shown
@@ -41,11 +41,11 @@ interface SandboxProps {
4141

4242
type SandboxViewProps = {
4343
fullWidth?: boolean;
44-
sandboxProps: SandboxProps;
44+
sandboxProps: SandboxProps<unknown>;
4545
background?: string;
4646
};
4747

48-
export const Sandbox = (props: SandboxProps) => {
48+
export const Sandbox = <T = Record<string, unknown>,>(props: SandboxProps<T>) => {
4949
const {language: lang, version} = useContext(LanguageVersionContext);
5050
const [formatLang, setFormatLang] = useState<string>("");
5151

@@ -92,10 +92,20 @@ export const Sandbox = (props: SandboxProps) => {
9292
}
9393

9494
function onChangeFormItemBindings(bindings: ComponentBinding[]) {
95-
props.onChangeFormItemBindings?.(bindings, toKeyValue(bindings));
95+
props.onChangeFormItemBindings?.(bindings, toFormItemKeyValue(bindings));
9696
}
9797

98-
function toKeyValue(bindings: ComponentBinding[]) {
98+
function toKeyValue(bindings: ComponentBinding[]): T {
99+
return bindings.reduce((acc: Record<string, unknown>, prop: ComponentBinding) => {
100+
if (typeof prop.value === "string" && prop.value === "") {
101+
return acc;
102+
}
103+
acc[prop.name] = prop.value;
104+
return acc;
105+
}, {}) as unknown as T;
106+
}
107+
108+
function toFormItemKeyValue(bindings: ComponentBinding[]): Record<string, unknown> {
99109
return bindings.reduce((acc: Record<string, unknown>, prop: ComponentBinding) => {
100110
if (typeof prop.value === "string" && prop.value === "") {
101111
return acc;
@@ -117,7 +127,7 @@ export const Sandbox = (props: SandboxProps) => {
117127

118128
return (
119129
<>
120-
{props.skipRenderDom ? null : <SandboxView fullWidth={props.fullWidth} sandboxProps={props} background={props.background} />}
130+
{props.skipRenderDom ? null : <SandboxView fullWidth={props.fullWidth} sandboxProps={props as SandboxProps<unknown>} background={props.background} />}
121131

122132
{/* Only render the GoAAccordion if props.properties is provided */}
123133
{props.properties && props.properties.length > 0 && (
@@ -141,7 +151,7 @@ export const Sandbox = (props: SandboxProps) => {
141151
/>
142152
</GoabAccordion>
143153
)}
144-
<SandboxCode props={props} formatLang={formatLang} lang={lang} serializers={serializers} version={version} />
154+
<SandboxCode props={props as SandboxProps<unknown>} formatLang={formatLang} lang={lang} serializers={serializers} version={version} />
145155
{props.note &&
146156
(typeof props.note === "string" ? (
147157
<p className="sandbox-note">{props.note}</p>
@@ -162,7 +172,7 @@ export const Sandbox = (props: SandboxProps) => {
162172
};
163173

164174
type SandboxCodeProps = {
165-
props: SandboxProps & { children: ReactNode };
175+
props: SandboxProps<unknown> & { children: ReactNode };
166176
lang: string;
167177
formatLang: string;
168178
serializers: Record<string, Serializer>;
@@ -246,7 +256,7 @@ function SandboxCode(p: SandboxCodeProps) {
246256
// to be displayed, while hiding the non-reactive ones
247257
type AdditionalCodeSnippetsProps = {
248258
tags: string[];
249-
sandboxProps: SandboxProps;
259+
sandboxProps: SandboxProps<unknown>;
250260
}
251261
function AdditionalCodeSnippets(props: AdditionalCodeSnippetsProps) {
252262
const matches = (list: string[]): boolean => {
@@ -275,7 +285,7 @@ function AdditionalCodeSnippets(props: AdditionalCodeSnippetsProps) {
275285
// Filters components from within the Sandbox children
276286
// i.e. Get all the <CodeSnippet> components
277287
type ComponentListProps = {
278-
sandboxProps: SandboxProps;
288+
sandboxProps: SandboxProps<unknown>;
279289
type: ComponentType;
280290
}
281291
function ComponentList(props: ComponentListProps): ReactElement[] {
@@ -295,7 +305,7 @@ function ComponentList(props: ComponentListProps): ReactElement[] {
295305
type ComponentOutputProps = {
296306
formatLang: string;
297307
type: "angular" | "angular-reactive" | "angular-template-driven" | "react";
298-
sandboxProps: SandboxProps;
308+
sandboxProps: SandboxProps<unknown>;
299309
serializer: Serializer;
300310
}
301311

src/examples/show-links-to-navigation-items.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ import { propsToString } from "@components/sandbox/BaseSerializer.ts";
1212

1313
type FooterNavPropsType = GoabFooterNavSectionProps;
1414
type FooterPropsType = GoabAppFooterProps;
15-
type CastingType = {
16-
// add any required props here
17-
[key: string]: unknown;
18-
};
1915

2016
export const ShowLinksToNavigationItems = () => {
2117
const {version} = useContext(LanguageVersionContext);
@@ -55,8 +51,8 @@ export const ShowLinksToNavigationItems = () => {
5551
maxColumnCount: props.maxColumnCount || 1
5652
};
5753

58-
setFooterProps(footerProps as CastingType);
59-
setFooterNavSectionProps(footerNavSectionProps as CastingType);
54+
setFooterProps(footerProps as FooterPropsType);
55+
setFooterNavSectionProps(footerNavSectionProps as FooterNavPropsType);
6056
setAppFooterNavBindings(bindings);
6157
}
6258

src/hooks/useSandboxFormItem.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export const useSandboxFormItem = (initialProps: GoabFormItemProps) => {
4343

4444
function onFormItemChange(bindings: ComponentBinding[], props: Record<string, unknown>) {
4545
setFormItemBindings(bindings);
46-
setFormItemProps(props);
46+
setFormItemProps(props as GoabFormItemProps);
4747
}
4848

4949
return { formItemBindings, formItemProps, onFormItemChange };

src/routes/components/Accordion.tsx

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import { Category, ComponentHeader } from "@components/component-header/Componen
1414
import { useState } from "react";
1515
import AccordionExamples from "@examples/accordion/AccordionExamples.tsx";
1616
import { ComponentContent } from "@components/component-content/ComponentContent";
17-
import { GoabAccordionHeadingSize } from "@abgov/ui-components-common";
1817
import {
1918
LegacyMarginProperty,
2019
LegacyTestIdProperties,
@@ -35,12 +34,6 @@ const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%9
3534

3635

3736
type ComponentPropsType = GoabAccordionProps;
38-
type CastingType = {
39-
heading: string;
40-
headingSize: GoabAccordionHeadingSize;
41-
children: React.ReactNode;
42-
[key: string]: unknown;
43-
};
4437

4538
export default function AccordionPage() {
4639

@@ -215,9 +208,9 @@ export default function AccordionPage() {
215208
MarginProperty,
216209
];
217210

218-
function onSandboxChange(bindings: ComponentBinding[], props: Record<string, unknown>) {
211+
function onSandboxChange(bindings: ComponentBinding[], props: ComponentPropsType) {
219212
setAccordionBindings(bindings);
220-
setAccordionProps(props as CastingType);
213+
setAccordionProps(props);
221214
}
222215

223216
return (
@@ -237,7 +230,7 @@ export default function AccordionPage() {
237230
<h2 id="component" style={{ display: "none" }}>
238231
Playground
239232
</h2>
240-
<Sandbox properties={accordionBindings} onChange={onSandboxChange} fullWidth>
233+
<Sandbox<ComponentPropsType> properties={accordionBindings} onChange={onSandboxChange} fullWidth>
241234
<GoabAccordion {...accordionProps}>
242235
This is the content in an accordion item. This content can be anything that you want including rich
243236
text, components, and more.

src/routes/components/AppFooter.tsx

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,6 @@ const relatedComponents = [
3030
];
3131
type ComponentPropsType = GoabAppFooterProps;
3232

33-
type CastingType = {
34-
// add any required props here
35-
[key: string]: unknown;
36-
};
37-
3833
export default function AppFooterPage() {
3934
const {language} = useContext(LanguageVersionContext);
4035

@@ -88,8 +83,8 @@ export default function AppFooterPage() {
8883
},
8984
];
9085

91-
function onSandbox1Change(bindings: ComponentBinding[], props: Record<string, unknown>) {
92-
setSandbox1Props(props as CastingType);
86+
function onSandbox1Change(bindings: ComponentBinding[], props: ComponentPropsType) {
87+
setSandbox1Props(props);
9388
setAppFooterBindings(bindings);
9489
}
9590

@@ -110,7 +105,7 @@ export default function AppFooterPage() {
110105
Playground
111106
</h2>
112107
<h3>Basic Footer</h3>
113-
<Sandbox properties={appFooterBindings} onChange={onSandbox1Change} fullWidth>
108+
<Sandbox<ComponentPropsType> properties={appFooterBindings} onChange={onSandbox1Change} fullWidth>
114109
<GoabAppFooter {...sandbox1Props} />
115110
</Sandbox>
116111

src/routes/components/AppHeader.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ const relatedComponents = [
2929
{ link: "/components/microsite-header", name: "Microsite header" }
3030
];
3131
type ComponentPropsType = GoabAppHeaderProps;
32-
type CastingType = {
33-
// add any required props here
34-
[key: string]: unknown;
35-
};
3632
export default function AppHeaderPage() {
3733
const [appHeaderProps, setAppHeaderProps] = useState<ComponentPropsType>({
3834
url: "www.alberta.ca",
@@ -185,8 +181,8 @@ export default function AppHeaderPage() {
185181
];
186182

187183

188-
function onSandboxChange(bindings: ComponentBinding[], props: Record<string, unknown>) {
189-
setAppHeaderProps(props as CastingType);
184+
function onSandboxChange(bindings: ComponentBinding[], props: ComponentPropsType) {
185+
setAppHeaderProps(props);
190186
setAppHeaderBindings(bindings);
191187
}
192188

@@ -207,7 +203,7 @@ export default function AppHeaderPage() {
207203
<h2 id="component" style={{ display: "none" }}>
208204
Playground
209205
</h2>
210-
<Sandbox properties={appHeaderBindings} onChange={onSandboxChange} fullWidth>
206+
<Sandbox<ComponentPropsType> properties={appHeaderBindings} onChange={onSandboxChange} fullWidth>
211207
<GoabAppHeader {...appHeaderProps} />
212208
</Sandbox>
213209

src/routes/components/Badge.tsx

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
} from "@components/component-properties/ComponentProperties.tsx";
99
import { ComponentContent } from "@components/component-content/ComponentContent";
1010
import BadgeExamples from "@examples/badge/BadgeExamples.tsx";
11-
import { GoabBadgeType } from "@abgov/ui-components-common";
1211
import { DesignEmpty } from "@components/empty-states/design-empty/DesignEmpty.tsx";
1312
import { AccessibilityEmpty } from "@components/empty-states/accessibility-empty/AccessibilityEmpty.tsx";
1413
import ICONS from "@routes/components/icons.json";
@@ -37,12 +36,6 @@ const relatedComponents = [
3736
];
3837

3938
type ComponentPropsType = GoabBadgeProps;
40-
type CastingType = {
41-
// add any required props here
42-
type: GoabBadgeType;
43-
content: string;
44-
[key: string]: unknown;
45-
};
4639

4740
export default function BadgePage() {
4841
const [badgeProps, setBadgeProps] = useState<ComponentPropsType>({
@@ -203,9 +196,9 @@ export default function BadgePage() {
203196
},
204197
];
205198

206-
function onSandboxChange(badgeBindings: ComponentBinding[], props: Record<string, unknown>) {
199+
function onSandboxChange(badgeBindings: ComponentBinding[], props: ComponentPropsType) {
207200
setBadgeBindings(badgeBindings);
208-
setBadgeProps(props as CastingType);
201+
setBadgeProps(props);
209202
}
210203

211204
return (
@@ -224,7 +217,7 @@ export default function BadgePage() {
224217
<h2 id="component" style={{ display: "none" }}>
225218
Playground
226219
</h2>
227-
<Sandbox properties={badgeBindings} onChange={onSandboxChange}>
220+
<Sandbox<ComponentPropsType> properties={badgeBindings} onChange={onSandboxChange}>
228221
<GoabBadge {...badgeProps} />
229222
</Sandbox>
230223
<ComponentProperties

src/routes/components/Callout.tsx

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414
ComponentProperty,
1515
} from "@components/component-properties/ComponentProperties.tsx";
1616
import { ComponentContent } from "@components/component-content/ComponentContent";
17-
import { GoabCalloutType } from "@abgov/ui-components-common";
1817
import {
1918
LegacyMarginProperty,
2019
LegacyTestIdProperties, MarginProperty,
@@ -34,10 +33,6 @@ const relatedComponents = [
3433
{ link: "/components/notification-banner", name: "Notification banner" },
3534
];
3635
type ComponentPropsType = GoabCalloutProps;
37-
type CastingType = {
38-
type: GoabCalloutType;
39-
[key: string]: unknown;
40-
};
4136

4237
export default function CalloutPage() {
4338
const [componentProps, setComponentProps] = useState<ComponentPropsType>({
@@ -163,9 +158,9 @@ export default function CalloutPage() {
163158
MarginProperty,
164159
];
165160

166-
function onSandboxChange(bindings: ComponentBinding[], props: Record<string, unknown>) {
161+
function onSandboxChange(bindings: ComponentBinding[], props: ComponentPropsType) {
167162
setComponentBindings(bindings);
168-
setComponentProps(props as CastingType);
163+
setComponentProps(props);
169164
}
170165

171166
return (
@@ -185,7 +180,7 @@ export default function CalloutPage() {
185180
<h2 id="component" style={{ display: "none" }}>
186181
Playground
187182
</h2>
188-
<Sandbox properties={componentBindings} onChange={onSandboxChange}>
183+
<Sandbox<ComponentPropsType> properties={componentBindings} onChange={onSandboxChange}>
189184
<GoabCallout {...componentProps}>
190185
Callout important information for the user.
191186
</GoabCallout>

src/routes/components/Checkbox.tsx

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,6 @@ const relatedComponents = [
3030
];
3131
const FIGMA_LINK = "https://www.figma.com/design/3pb2IK8s2QUqWieH79KdN7/%E2%9D%96-Component-library-%7C-DDD?node-id=183-219";
3232
type ComponentPropsType = GoabCheckboxProps;
33-
type CastingType = {
34-
name: string;
35-
checked: boolean;
36-
[key: string]: unknown;
37-
};
3833
export default function CheckboxPage() {
3934
const {version} = useContext(LanguageVersionContext);
4035
const [checkboxProps, setCheckboxProps] = useState<ComponentPropsType>({
@@ -241,7 +236,7 @@ export default function CheckboxPage() {
241236

242237
const noop = () => {};
243238

244-
function onChange(bindings: ComponentBinding[], props: Record<string, unknown>) {
239+
function onChange(bindings: ComponentBinding[], props: ComponentPropsType) {
245240
const missingProps = {
246241
name: "item",
247242
checked: false,
@@ -250,7 +245,7 @@ export default function CheckboxPage() {
250245
const updatedProps = { ...missingProps, ...props };
251246

252247
setCheckboxBindings(bindings);
253-
setCheckboxProps(updatedProps as CastingType);
248+
setCheckboxProps(updatedProps);
254249
}
255250

256251
return (
@@ -268,7 +263,7 @@ export default function CheckboxPage() {
268263
<GoabTabs initialTab={1}>
269264
<GoabTab heading="Code playground">
270265
<h2 id="component" style={{ display: "none" }}>Playground</h2>
271-
<Sandbox
266+
<Sandbox<ComponentPropsType>
272267
properties={checkboxBindings}
273268
formItemProperties={formItemBindings}
274269
onChange={onChange}

src/routes/components/Container.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@ const relatedComponents = [
2727
{ link: "/components/divider", name: "Divider" }
2828
];
2929
type ComponentPropsType = GoabContainerProps;
30-
type CastingType = {
31-
[key: string]: unknown;
32-
};
3330

3431
export default function ContainerPage() {
3532
const [containerProps, setContainerProps] = useState<ComponentPropsType>({});
@@ -213,9 +210,9 @@ export default function ContainerPage() {
213210
},
214211
];
215212

216-
function onSandboxChange(bindings: ComponentBinding[], props: Record<string, unknown>) {
213+
function onSandboxChange(bindings: ComponentBinding[], props: ComponentPropsType) {
217214
setContainerBindings(bindings);
218-
setContainerProps(props as CastingType);
215+
setContainerProps(props);
219216
}
220217

221218
return (
@@ -234,7 +231,7 @@ export default function ContainerPage() {
234231
<GoabTabs initialTab={1}>
235232
<GoabTab heading="Code playground">
236233
<h2 id="component" style={{ display: "none" }}>Playground</h2>
237-
<Sandbox properties={containerBindings} onChange={onSandboxChange} fullWidth>
234+
<Sandbox<ComponentPropsType> properties={containerBindings} onChange={onSandboxChange} fullWidth>
238235
<GoabContainer {...containerProps}>
239236
<h2>Detach to use</h2>
240237
<p>Add things inside me</p>

0 commit comments

Comments
 (0)