Skip to content

Instantly share code, notes, and snippets.

@pabletecodes
Last active June 29, 2024 16:03
Show Gist options
  • Save pabletecodes/d7f1df2c4ff2beeb06ddab238dd203fd to your computer and use it in GitHub Desktop.
Save pabletecodes/d7f1df2c4ff2beeb06ddab238dd203fd to your computer and use it in GitHub Desktop.
Design Challenge 003: Generating Context Menu from Legacy API Response
// Your challenge is to find ways to simplify this code further.
// You can use any patterns or refactoring techniques you find useful.
// Think about improving readability and maintainability while ensuring the
// functionality remains intact.
// Backend response
{
"operations": [
{
"internal_legacy_field": "assign-issue",
"name": "Assign",
"desc": "Assign this issue to someone",
"url": "/issues/assign"
},
{
"internal_legacy_field": "assign-to-me",
"name": "Assign to me",
"desc": "Assign this issue to me",
"url": "/issues/assign-to-me/very/old/outdated/link"
},
{
"internal_legacy_field": "comment-issue",
"name": "Comment",
"desc": "Comment on this issue",
"url": "/comment/issue"
},
{
"internal_legacy_field": "log-work",
"name": "Log work",
"desc": "Log work against this issue"
}
]
}
// Code
const URL_OVERRIDE_FIELDS: Record<string, string> = {
"comment-issue": "/issues/comment",
};
// There are more in the real product
const SKIP_FIELDS: string[] = ["log-work"];
const DIALOG_FIELDS: string[] = ["assign-issue", "comment-issue"];
const IssueContextMenu = ({ data }: { data: IssueResponse }) => {
return (
<DropdownMenu>
{data.operations.reduce(
(result, { name, url: givenUrl, internal_legacy_field }) => {
let url = givenUrl;
if (Object.hasOwn(URL_OVERRIDE_FIELDS, internal_legacy_field)) {
url = URL_OVERRIDE_FIELDS[internal_legacy_field];
}
if (!SKIP_FIELDS.includes(internal_legacy_field)) {
result.push(
DIALOG_FIELDS.includes(internal_legacy_field) ? (
<ButtonMenuItem key={name} name={name} />
) : (
<LinkMenuItem key={name} name={name} url={url ?? ""} />
),
);
}
return result;
},
[] as React.ReactNode[],
)}
</DropdownMenu>
);
};
// issue-context-menu
const DIALOG_FIELDS: string[] = ['assign-issue', 'comment-issue'];
function isDialogField({ url, internal_legacy_field }): boolean {
return DIALOG_FIELDS.includes(internal_legacy_field);
}
export const IssueContextMenu = ({ data }: { data: IssueResponse }) => {
const { operations } = adaptIssueResponse(data);
return (
<DropdownMenu>
{operations.map(operation => {
return isDialogField(operation) ? (
<ButtonMenuItem key={operation.name} name={operation.name} />
) : (
<LinkMenuItem
key={operation.name}
name={operation.name}
url={operation.url}
/>
);
})}
</DropdownMenu>
);
};
// adapt-issue-response
function adaptIssueResponse(issueResponse: IssueResponse): IssueResponse {
return {
...issueResponse,
operations: issueResponse.operations
.filter(operation => !skip(operation))
.map(overrideURL),
};
}
const SKIP_FIELDS: string[] = ['log-work'];
function skip({ internal_legacy_field }) {
return SKIP_FIELDS.includes(internal_legacy_field);
}
const URL_OVERRIDE_FIELDS: Record<string, string> = {
'comment-issue': '/issues/comment',
};
function overrideURL({ name, url: givenUrl, internal_legacy_field }) {
let url = givenUrl ?? '';
if (Object.hasOwn(URL_OVERRIDE_FIELDS, internal_legacy_field)) {
url = URL_OVERRIDE_FIELDS[internal_legacy_field];
}
return { name, url, internal_legacy_field };
}
// boilerplate
type IssueResponse = {
operations: Array<IssueResponseOperation>;
};
type IssueResponseOperation = {
name: string;
url: string;
internal_legacy_field: string
};
const DropdownMenu = props => <div>{props.children}</div>;
const ButtonMenuItem = props => <div>Button - {props.name}</div>;
const LinkMenuItem = props => (
<div>
LinkMenuItem - {props.name} - {props.url}
</div>
);
const MENU_ITEM_COMPONENTS = [
{
condition: isDialogField,
component: ButtonMenuItem,
getProps: (operation) => {
return {
key: operation.name,
name: operation.name
}
}
},
{
condition: () => true,
component: LinkMenuItem,
getProps: (operation) => {
return {
key: operation.name,
name: operation.name,
url: operation.url
}
}
}
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment